Browse code

Rebased.

David Sissitka authored on 2013/09/11 17:03:17
Showing 77 changed files
... ...
@@ -17,6 +17,7 @@ Antony Messerli <amesserl@rackspace.com>
17 17
 Barry Allard <barry.allard@gmail.com>
18 18
 Brandon Liu <bdon@bdon.org>
19 19
 Brian McCallister <brianm@skife.org>
20
+Brian Olsen <brian@maven-group.org>
20 21
 Bruno Bigras <bigras.bruno@gmail.com>
21 22
 Caleb Spare <cespare@gmail.com>
22 23
 Calen Pennington <cale@edx.org>
... ...
@@ -34,6 +35,7 @@ Dominik Honnef <dominik@honnef.co>
34 34
 Don Spaulding <donspauldingii@gmail.com>
35 35
 Dr Nic Williams <drnicwilliams@gmail.com>
36 36
 Elias Probst <mail@eliasprobst.eu>
37
+Emily Rose <emily@contactvibe.com>
37 38
 Eric Hanchrow <ehanchrow@ine.com>
38 39
 Eric Myhre <hash@exultant.us>
39 40
 Erno Hopearuoho <erno.hopearuoho@gmail.com>
... ...
@@ -73,6 +75,7 @@ Louis Opter <kalessin@kalessin.fr>
73 73
 Marco Hennings <marco.hennings@freiheit.com>
74 74
 Marcus Farkas <toothlessgear@finitebox.com>
75 75
 Mark McGranaghan <mmcgrana@gmail.com>
76
+Martin Redmond <mrtodo@gmail.com>
76 77
 Maxim Treskin <zerthurd@gmail.com>
77 78
 meejah <meejah@meejah.ca>
78 79
 Michael Crosby <crosby.michael@gmail.com>
... ...
@@ -103,6 +106,7 @@ Solomon Hykes <solomon@dotcloud.com>
103 103
 Sridhar Ratnakumar <sridharr@activestate.com>
104 104
 Stefan Praszalowicz <stefan@greplin.com>
105 105
 Thatcher Peskens <thatcher@dotcloud.com>
106
+Thijs Terlouw <thijsterlouw@gmail.com>
106 107
 Thomas Bikeev <thomas.bikeev@mac.com>
107 108
 Thomas Hansen <thomas.hansen@gmail.com>
108 109
 Tianon Gravi <admwiggin@gmail.com>
... ...
@@ -27,7 +27,7 @@ run	/bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_
27 27
 run	PKG=github.com/kr/pty REV=27435c699;		 git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV
28 28
 run	PKG=github.com/gorilla/context/ REV=708054d61e5; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV
29 29
 run	PKG=github.com/gorilla/mux/ REV=9b36453141c;	 git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV
30
-run	PKG=github.com/dotcloud/tar/ REV=d06045a6d9;	 git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV
30
+run	PKG=github.com/dotcloud/tar/ REV=e5ea6bb21a3294;	 git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV
31 31
 run	PKG=code.google.com/p/go.net/ REV=84a4013f96e0;  hg  clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && hg  checkout    $REV
32 32
 # Upload docker source
33 33
 add	.       /go/src/github.com/dotcloud/docker
... ...
@@ -1,8 +1,8 @@
1 1
 Docker: the Linux container engine
2 2
 ==================================
3 3
 
4
-Docker is an open-source engine which automates the deployment of
5
-applications as highly portable, self-sufficient containers.
4
+Docker is an open source project to pack, ship and run any application
5
+as a lightweight container
6 6
 
7 7
 Docker containers are both *hardware-agnostic* and
8 8
 *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
18 18
 accumulated over several years of large-scale operation and support of
19 19
 hundreds of thousands of applications and databases.
20 20
 
21
-![Docker L](docs/sources/concepts/images/dockerlogo-h.png "Docker")
21
+![Docker L](docs/sources/static_files/dockerlogo-h.png "Docker")
22 22
 
23 23
 ## Better than VMs
24 24
 
... ...
@@ -140,125 +140,25 @@ Note that Docker doesn't care *how* dependencies are built - as long
140 140
 as they can be built by running a Unix command in a container.
141 141
 
142 142
 
143
-Install instructions
144
-==================
143
+Getting started
144
+===============
145 145
 
146
-Quick install on Ubuntu 12.04 and 12.10
146
+Docker can be installed on your local machine as well as servers - both bare metal and virtualized.
147
+It is available as a binary on most modern Linux systems, or as a VM on Windows, Mac and other systems.
147 148
 
148
-```bash
149
-curl https://get.docker.io | sudo sh -x
150
-```
149
+We also offer an interactive tutorial for quickly learning the basics of using Docker.
151 150
 
152
-Binary installs
153 151
 
154
-Docker supports the following binary installation methods.  Note that
155
-some methods are community contributions and not yet officially
156
-supported.
152
+For up-to-date install instructions and online tutorials, see the [Getting Started page](http://www.docker.io/gettingstarted/).
157 153
 
158
-* [Ubuntu 12.04 and 12.10 (officially supported)](http://docs.docker.io/en/latest/installation/ubuntulinux/)
159
-* [Arch Linux](http://docs.docker.io/en/latest/installation/archlinux/)
160
-* [Mac OS X (with Vagrant)](http://docs.docker.io/en/latest/installation/vagrant/)
161
-* [Windows (with Vagrant)](http://docs.docker.io/en/latest/installation/windows/)
162
-* [Amazon EC2 (with Vagrant)](http://docs.docker.io/en/latest/installation/amazon/)
163 154
 
164 155
 Usage examples
165 156
 ==============
166 157
 
167
-First run the ``docker`` daemon
168
-
169
-All the examples assume your machine is running the ``docker``
170
-daemon. To run the ``docker`` daemon in the background, simply type:
171
-
172
-```bash
173
-# On a production system you want this running in an init script
174
-sudo docker -d &
175
-```
176
-
177
-Now you can run ``docker`` in client mode: all commands will be
178
-forwarded to the ``docker`` daemon, so the client can run from any
179
-account.
180
-
181
-```bash
182
-# Now you can run docker commands from any account.
183
-docker help
184
-```
185
-
186
-
187
-Throwaway shell in a base Ubuntu image
188
-
189
-```bash
190
-docker pull ubuntu:12.10
191
-
192
-# Run an interactive shell, allocate a tty, attach stdin and stdout
193
-# To detach the tty without exiting the shell, use the escape sequence Ctrl-p + Ctrl-q
194
-docker run -i -t ubuntu:12.10 /bin/bash
195
-```
196
-
197
-Starting a long-running worker process
198
-
199
-```bash
200
-# Start a very useful long-running process
201
-JOB=$(docker run -d ubuntu /bin/sh -c "while true; do echo Hello world; sleep 1; done")
202
-
203
-# Collect the output of the job so far
204
-docker logs $JOB
205
-
206
-# Kill the job
207
-docker kill $JOB
208
-```
209
-
210
-Running an irc bouncer
211
-
212
-```bash
213
-BOUNCER_ID=$(docker run -d -p 6667 -u irc shykes/znc zncrun $USER $PASSWORD)
214
-echo "Configure your irc client to connect to port $(docker port $BOUNCER_ID 6667) of this machine"
215
-```
216
-
217
-Running Redis
218
-
219
-```bash
220
-REDIS_ID=$(docker run -d -p 6379 shykes/redis redis-server)
221
-echo "Configure your redis client to connect to port $(docker port $REDIS_ID 6379) of this machine"
222
-```
158
+Docker can be used to run short-lived commands, long-running daemons (app servers, databases etc.),
159
+interactive shell sessions, etc.
223 160
 
224
-Share your own image!
225
-
226
-```bash
227
-CONTAINER=$(docker run -d ubuntu:12.10 apt-get install -y curl)
228
-docker commit -m "Installed curl" $CONTAINER $USER/betterbase
229
-docker push $USER/betterbase
230
-```
231
-
232
-A list of publicly available images is [available
233
-here](https://github.com/dotcloud/docker/wiki/Public-docker-images).
234
-
235
-Expose a service on a TCP port
236
-
237
-```bash
238
-# Expose port 4444 of this container, and tell netcat to listen on it
239
-JOB=$(docker run -d -p 4444 base /bin/nc -l -p 4444)
240
-
241
-# Which public port is NATed to my container?
242
-PORT=$(docker port $JOB 4444)
243
-
244
-# Connect to the public port via the host's public address
245
-# Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work.
246
-# Replace *eth0* according to your local interface name.
247
-IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }')
248
-echo hello world | nc $IP $PORT
249
-
250
-# Verify that the network connection worked
251
-echo "Daemon received: $(docker logs $JOB)"
252
-```
161
+You can find a [list of real-world examples](http://docs.docker.io/en/latest/examples/) in the documentation.
253 162
 
254 163
 Under the hood
255 164
 --------------
... ...
@@ -288,142 +188,6 @@ They are probably not perfect, please let us know if anything feels
288 288
 wrong or incomplete.
289 289
 
290 290
 
291
-Note
292
-
293
-We also keep the documentation in this repository. The website
294
-documentation is generated using Sphinx using these sources.  Please
295
-find it under docs/sources/ and read more about it
296
-https://github.com/dotcloud/docker/tree/master/docs/README.md
297
-
298
-Please feel free to fix / update the documentation and send us pull
299
-requests. More tutorials are also welcome.
300
-
301
-
302
-Setting up a dev environment
303
-
304
-Instructions that have been verified to work on Ubuntu 12.10,
305
-
306
-```bash
307
-sudo apt-get -y install lxc curl xz-utils golang git mercurial
308
-
309
-export GOPATH=~/go/
310
-export PATH=$GOPATH/bin:$PATH
311
-
312
-mkdir -p $GOPATH/src/github.com/dotcloud
313
-cd $GOPATH/src/github.com/dotcloud
314
-git clone https://github.com/dotcloud/docker.git
315
-cd docker
316
-
317
-go get -v github.com/dotcloud/docker/...
318
-go install -v github.com/dotcloud/docker/...
319
-```
320
-
321
-Then run the docker daemon,
322
-
323
-```bash
324
-sudo $GOPATH/bin/docker -d
325
-```
326
-
327
-Run the `go install` command (above) to recompile docker.
328
-
329
-
330
-What is a Standard Container?
331
-=============================
332
-
333
-Docker defines a unit of software delivery called a Standard
334
-Container. The goal of a Standard Container is to encapsulate a
335
-software component and all its dependencies in a format that is
336
-self-describing and portable, so that any compliant runtime can run it
337
-without extra dependencies, regardless of the underlying machine and
338
-the contents of the container.
339
-
340
-The spec for Standard Containers is currently a work in progress, but
341
-it is very straightforward. It mostly defines 1) an image format, 2) a
342
-set of standard operations, and 3) an execution environment.
343
-
344
-A great analogy for this is the shipping container. Just like how
345
-Standard Containers are a fundamental unit of software delivery,
346
-shipping containers are a fundamental unit of physical delivery.
347
-
348
-### 1. STANDARD OPERATIONS
349
-
350
-Just like shipping containers, Standard Containers define a set of
351
-STANDARD OPERATIONS. Shipping containers can be lifted, stacked,
352
-locked, loaded, unloaded and labelled. Similarly, Standard Containers
353
-can be started, stopped, copied, snapshotted, downloaded, uploaded and
354
-tagged.
355
-
356
-
357
-### 2. CONTENT-AGNOSTIC
358
-
359
-Just like shipping containers, Standard Containers are
360
-CONTENT-AGNOSTIC: all standard operations have the same effect
361
-regardless of the contents. A shipping container will be stacked in
362
-exactly the same way whether it contains Vietnamese powder coffee or
363
-spare Maserati parts. Similarly, Standard Containers are started or
364
-uploaded in the same way whether they contain a postgres database, a
365
-php application with its dependencies and application server, or Java
366
-build artifacts.
367
-
368
-
369
-### 3. INFRASTRUCTURE-AGNOSTIC
370
-
371
-Both types of containers are INFRASTRUCTURE-AGNOSTIC: they can be
372
-transported to thousands of facilities around the world, and
373
-manipulated by a wide variety of equipment. A shipping container can
374
-be packed in a factory in Ukraine, transported by truck to the nearest
375
-routing center, stacked onto a train, loaded into a German boat by an
376
-Australian-built crane, stored in a warehouse at a US facility,
377
-etc. Similarly, a standard container can be bundled on my laptop,
378
-uploaded to S3, downloaded, run and snapshotted by a build server at
379
-Equinix in Virginia, uploaded to 10 staging servers in a home-made
380
-Openstack cluster, then sent to 30 production instances across 3 EC2
381
-regions.
382
-
383
-
384
-### 4. DESIGNED FOR AUTOMATION
385
-
386
-Because they offer the same standard operations regardless of content
387
-and infrastructure, Standard Containers, just like their physical
388
-counterparts, are extremely well-suited for automation. In fact, you
389
-could say automation is their secret weapon.
390
-
391
-Many things that once required time-consuming and error-prone human
392
-effort can now be programmed. Before shipping containers, a bag of
393
-powder coffee was hauled, dragged, dropped, rolled and stacked by 10
394
-different people in 10 different locations by the time it reached its
395
-destination. 1 out of 50 disappeared. 1 out of 20 was damaged. The
396
-process was slow, inefficient and cost a fortune - and was entirely
397
-different depending on the facility and the type of goods.
398
-
399
-Similarly, before Standard Containers, by the time a software
400
-component ran in production, it had been individually built,
401
-configured, bundled, documented, patched, vendored, templated, tweaked
402
-and instrumented by 10 different people on 10 different
403
-computers. Builds failed, libraries conflicted, mirrors crashed,
404
-post-it notes were lost, logs were misplaced, cluster updates were
405
-half-broken. The process was slow, inefficient and cost a fortune -
406
-and was entirely different depending on the language and
407
-infrastructure provider.
408
-
409
-
410
-### 5. INDUSTRIAL-GRADE DELIVERY
411
-
412
-There are 17 million shipping containers in existence, packed with
413
-every physical good imaginable. Every single one of them can be loaded
414
-onto the same boats, by the same cranes, in the same facilities, and
415
-sent anywhere in the World with incredible efficiency. It is
416
-embarrassing to think that a 30 ton shipment of coffee can safely
417
-travel half-way across the World in *less time* than it takes a
418
-software team to deliver its code from one datacenter to another
419
-sitting 10 miles away.
420
-
421
-With Standard Containers we can put an end to that embarrassment, by
422
-making INDUSTRIAL-GRADE DELIVERY of software a reality.
423
-
424
-
425 291
 ### Legal
426 292
 
427 293
 Transfers of Docker shall be in accordance with applicable export
... ...
@@ -12,23 +12,20 @@ Vagrant::Config.run do |config|
12 12
   # Setup virtual machine box. This VM configuration code is always executed.
13 13
   config.vm.box = BOX_NAME
14 14
   config.vm.box_url = BOX_URI
15
-  config.vm.forward_port 4243, 4243
16 15
 
17
-  # Provision docker and new kernel if deployment was not done
16
+  # Provision docker and new kernel if deployment was not done.
17
+  # It is assumed Vagrant can successfully launch the provider instance.
18 18
   if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty?
19 19
     # Add lxc-docker package
20
-    pkg_cmd = "wget -q -O - http://get.docker.io/gpg | apt-key add -;" \
21
-      "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list;" \
20
+    pkg_cmd = "wget -q -O - https://get.docker.io/gpg | apt-key add -;" \
21
+      "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list;" \
22 22
       "apt-get update -qq; apt-get install -q -y --force-yes lxc-docker; "
23
-    # Add X.org Ubuntu backported 3.8 kernel
24
-    pkg_cmd << "apt-get update -qq; apt-get install -q -y python-software-properties; " \
25
-      "add-apt-repository -y ppa:ubuntu-x-swat/r-lts-backport; " \
26
-      "apt-get update -qq; apt-get install -q -y linux-image-3.8.0-19-generic; "
27
-    # Add guest additions if local vbox VM
28
-    is_vbox = true
29
-    ARGV.each do |arg| is_vbox &&= !arg.downcase.start_with?("--provider") end
30
-    if is_vbox
31
-      pkg_cmd << "apt-get install -q -y linux-headers-3.8.0-19-generic dkms; " \
23
+    # Add Ubuntu raring backported kernel
24
+    pkg_cmd << "apt-get update -qq; apt-get install -q -y linux-image-generic-lts-raring; "
25
+    # Add guest additions if local vbox VM. As virtualbox is the default provider,
26
+    # it is assumed it won't be explicitly stated.
27
+    if ENV["VAGRANT_DEFAULT_PROVIDER"].nil? && ARGV.none? { |arg| arg.downcase.start_with?("--provider") }
28
+      pkg_cmd << "apt-get install -q -y linux-headers-generic-lts-raring dkms; " \
32 29
         "echo 'Downloading VBox Guest Additions...'; " \
33 30
         "wget -q http://dlc.sun.com.edgesuite.net/virtualbox/4.2.12/VBoxGuestAdditions_4.2.12.iso; "
34 31
       # Prepare the VM to add guest additions after reboot
... ...
@@ -2,6 +2,7 @@ package docker
2 2
 
3 3
 import (
4 4
 	"code.google.com/p/go.net/websocket"
5
+	"encoding/base64"
5 6
 	"encoding/json"
6 7
 	"fmt"
7 8
 	"github.com/dotcloud/docker/auth"
... ...
@@ -20,7 +21,7 @@ import (
20 20
 	"strings"
21 21
 )
22 22
 
23
-const APIVERSION = 1.4
23
+const APIVERSION = 1.5
24 24
 const DEFAULTHTTPHOST = "127.0.0.1"
25 25
 const DEFAULTHTTPPORT = 4243
26 26
 const DEFAULTUNIXSOCKET = "/var/run/docker.sock"
... ...
@@ -304,7 +305,16 @@ func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *h
304 304
 
305 305
 	outs := srv.Containers(all, size, n, since, before)
306 306
 
307
-	return writeJSON(w, http.StatusOK, outs)
307
+	if version < 1.5 {
308
+		outs2 := []APIContainersOld{}
309
+		for _, ctnr := range outs {
310
+			outs2 = append(outs2, ctnr.ToLegacy())
311
+		}
312
+
313
+        return writeJSON(w, http.StatusOK, outs2)
314
+	} else {
315
+        return writeJSON(w, http.StatusOK, outs)
316
+	}
308 317
 }
309 318
 
310 319
 func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -361,6 +371,16 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht
361 361
 	tag := r.Form.Get("tag")
362 362
 	repo := r.Form.Get("repo")
363 363
 
364
+	authEncoded := r.Header.Get("X-Registry-Auth")
365
+	authConfig := &auth.AuthConfig{}
366
+	if authEncoded != "" {
367
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
368
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
369
+			// for a pull it is not an error if no auth was given
370
+			// to increase compatibility with the existing api it is defaulting to be empty
371
+			authConfig = &auth.AuthConfig{}
372
+		}
373
+	}
364 374
 	if version > 1.0 {
365 375
 		w.Header().Set("Content-Type", "application/json")
366 376
 	}
... ...
@@ -372,7 +392,7 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht
372 372
 				metaHeaders[k] = v
373 373
 			}
374 374
 		}
375
-		if err := srv.ImagePull(image, tag, w, sf, &auth.AuthConfig{}, metaHeaders, version > 1.3); err != nil {
375
+		if err := srv.ImagePull(image, tag, w, sf, authConfig, metaHeaders, version > 1.3); err != nil {
376 376
 			if sf.Used() {
377 377
 				w.Write(sf.FormatError(err))
378 378
 				return nil
... ...
@@ -432,19 +452,32 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
432 432
 }
433 433
 
434 434
 func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
435
-	authConfig := &auth.AuthConfig{}
436 435
 	metaHeaders := map[string][]string{}
437 436
 	for k, v := range r.Header {
438 437
 		if strings.HasPrefix(k, "X-Meta-") {
439 438
 			metaHeaders[k] = v
440 439
 		}
441 440
 	}
442
-	if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
443
-		return err
444
-	}
445 441
 	if err := parseForm(r); err != nil {
446 442
 		return err
447 443
 	}
444
+	authConfig := &auth.AuthConfig{}
445
+
446
+	authEncoded := r.Header.Get("X-Registry-Auth")
447
+	if authEncoded != "" {
448
+		// the new format is to handle the authConfig as a header
449
+		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
450
+		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
451
+			// to increase compatibility to existing api it is defaulting to be empty
452
+			authConfig = &auth.AuthConfig{}
453
+		}
454
+	} else {
455
+		// the old format is supported for compatibility if there was no authConfig header
456
+		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
457
+			return err
458
+		}
459
+
460
+	}
448 461
 
449 462
 	if vars == nil {
450 463
 		return fmt.Errorf("Missing parameter")
... ...
@@ -772,6 +805,11 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r
772 772
 		return err
773 773
 	}
774 774
 
775
+	_, err = srv.ImageInspect(name)
776
+	if err == nil {
777
+		return fmt.Errorf("Conflict between containers and images")
778
+	}
779
+
775 780
 	return writeJSON(w, http.StatusOK, container)
776 781
 }
777 782
 
... ...
@@ -786,22 +824,9 @@ func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *htt
786 786
 		return err
787 787
 	}
788 788
 
789
-	return writeJSON(w, http.StatusOK, image)
790
-}
791
-
792
-func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
793
-	apiConfig := &APIImageConfig{}
794
-	if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
795
-		return err
796
-	}
797
-
798
-	image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config)
799
-	if err != nil {
800
-		return err
801
-	}
802
-	if image == nil {
803
-		w.WriteHeader(http.StatusNotFound)
804
-		return nil
789
+	_, err = srv.ContainerInspect(name)
790
+	if err == nil {
791
+		return fmt.Errorf("Conflict between containers and images")
805 792
 	}
806 793
 
807 794
 	return writeJSON(w, http.StatusOK, &APIID{ID: image.ID})
... ...
@@ -982,7 +1007,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
982 982
 			"/images/{name:.*}/insert":      postImagesInsert,
983 983
 			"/images/{name:.*}/push":        postImagesPush,
984 984
 			"/images/{name:.*}/tag":         postImagesTag,
985
-			"/images/getCache":              postImagesGetCache,
986 985
 			"/containers/create":            postContainersCreate,
987 986
 			"/containers/{name:.*}/kill":    postContainersKill,
988 987
 			"/containers/{name:.*}/restart": postContainersRestart,
... ...
@@ -1,5 +1,7 @@
1 1
 package docker
2 2
 
3
+import "encoding/json"
4
+
3 5
 type APIHistory struct {
4 6
 	ID        string   `json:"Id"`
5 7
 	Tags      []string `json:",omitempty"`
... ...
@@ -47,6 +49,30 @@ type APIContainers struct {
47 47
 	Command    string
48 48
 	Created    int64
49 49
 	Status     string
50
+	Ports      []APIPort
51
+	SizeRw     int64
52
+	SizeRootFs int64
53
+}
54
+
55
+func (self *APIContainers) ToLegacy() APIContainersOld {
56
+	return APIContainersOld{
57
+		ID: self.ID,
58
+		Image: self.Image,
59
+		Command: self.Command,
60
+		Created: self.Created,
61
+		Status: self.Status,
62
+		Ports: displayablePorts(self.Ports),
63
+		SizeRw: self.SizeRw,
64
+		SizeRootFs: self.SizeRootFs,
65
+	}
66
+}
67
+
68
+type APIContainersOld struct {
69
+	ID         string `json:"Id"`
70
+	Image      string
71
+	Command    string
72
+	Created    int64
73
+	Status     string
50 74
 	Ports      string
51 75
 	SizeRw     int64
52 76
 	SizeRootFs int64
... ...
@@ -67,7 +93,17 @@ type APIRun struct {
67 67
 }
68 68
 
69 69
 type APIPort struct {
70
-	Port string
70
+	PrivatePort int64
71
+	PublicPort  int64
72
+	Type        string
73
+}
74
+
75
+func (port *APIPort) MarshalJSON() ([]byte, error) {
76
+	return json.Marshal(map[string]interface{}{
77
+		"PrivatePort": port.PrivatePort,
78
+		"PublicPort":  port.PublicPort,
79
+		"Type":        port.Type,
80
+	})
71 81
 }
72 82
 
73 83
 type APIVersion struct {
... ...
@@ -68,7 +68,7 @@ func TestGetInfo(t *testing.T) {
68 68
 
69 69
 	srv := &Server{runtime: runtime}
70 70
 
71
-	initialImages, err := srv.runtime.graph.All()
71
+	initialImages, err := srv.runtime.graph.Map()
72 72
 	if err != nil {
73 73
 		t.Fatal(err)
74 74
 	}
... ...
@@ -321,7 +321,7 @@ func TestGetContainersJSON(t *testing.T) {
321 321
 
322 322
 	srv := &Server{runtime: runtime}
323 323
 
324
-	container, err := NewBuilder(runtime).Create(&Config{
324
+	container, err := runtime.Create(&Config{
325 325
 		Image: GetTestImage(runtime).ID,
326 326
 		Cmd:   []string{"echo", "test"},
327 327
 	})
... ...
@@ -357,10 +357,8 @@ func TestGetContainersExport(t *testing.T) {
357 357
 
358 358
 	srv := &Server{runtime: runtime}
359 359
 
360
-	builder := NewBuilder(runtime)
361
-
362 360
 	// Create a container and remove a file
363
-	container, err := builder.Create(
361
+	container, err := runtime.Create(
364 362
 		&Config{
365 363
 			Image: GetTestImage(runtime).ID,
366 364
 			Cmd:   []string{"touch", "/test"},
... ...
@@ -409,10 +407,8 @@ func TestGetContainersChanges(t *testing.T) {
409 409
 
410 410
 	srv := &Server{runtime: runtime}
411 411
 
412
-	builder := NewBuilder(runtime)
413
-
414 412
 	// Create a container and remove a file
415
-	container, err := builder.Create(
413
+	container, err := runtime.Create(
416 414
 		&Config{
417 415
 			Image: GetTestImage(runtime).ID,
418 416
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
... ...
@@ -449,6 +445,7 @@ func TestGetContainersChanges(t *testing.T) {
449 449
 }
450 450
 
451 451
 func TestGetContainersTop(t *testing.T) {
452
+	t.Skip("Fixme. Skipping test for now. Reported error when testing using dind: 'api_test.go:527: Expected 2 processes, found 0.'")
452 453
 	runtime, err := newTestRuntime()
453 454
 	if err != nil {
454 455
 		t.Fatal(err)
... ...
@@ -457,9 +454,7 @@ func TestGetContainersTop(t *testing.T) {
457 457
 
458 458
 	srv := &Server{runtime: runtime}
459 459
 
460
-	builder := NewBuilder(runtime)
461
-
462
-	container, err := builder.Create(
460
+	container, err := runtime.Create(
463 461
 		&Config{
464 462
 			Image:     GetTestImage(runtime).ID,
465 463
 			Cmd:       []string{"/bin/sh", "-c", "cat"},
... ...
@@ -540,10 +535,8 @@ func TestGetContainersByName(t *testing.T) {
540 540
 
541 541
 	srv := &Server{runtime: runtime}
542 542
 
543
-	builder := NewBuilder(runtime)
544
-
545 543
 	// Create a container and remove a file
546
-	container, err := builder.Create(
544
+	container, err := runtime.Create(
547 545
 		&Config{
548 546
 			Image: GetTestImage(runtime).ID,
549 547
 			Cmd:   []string{"echo", "test"},
... ...
@@ -573,10 +566,9 @@ func TestPostCommit(t *testing.T) {
573 573
 
574 574
 	srv := &Server{runtime: runtime}
575 575
 
576
-	builder := NewBuilder(runtime)
577 576
 
578 577
 	// Create a container and remove a file
579
-	container, err := builder.Create(
578
+	container, err := runtime.Create(
580 579
 		&Config{
581 580
 			Image: GetTestImage(runtime).ID,
582 581
 			Cmd:   []string{"touch", "/test"},
... ...
@@ -670,7 +662,7 @@ func TestPostContainersKill(t *testing.T) {
670 670
 
671 671
 	srv := &Server{runtime: runtime}
672 672
 
673
-	container, err := NewBuilder(runtime).Create(
673
+	container, err := runtime.Create(
674 674
 		&Config{
675 675
 			Image:     GetTestImage(runtime).ID,
676 676
 			Cmd:       []string{"/bin/cat"},
... ...
@@ -712,7 +704,7 @@ func TestPostContainersRestart(t *testing.T) {
712 712
 
713 713
 	srv := &Server{runtime: runtime}
714 714
 
715
-	container, err := NewBuilder(runtime).Create(
715
+	container, err := runtime.Create(
716 716
 		&Config{
717 717
 			Image:     GetTestImage(runtime).ID,
718 718
 			Cmd:       []string{"/bin/cat"},
... ...
@@ -766,7 +758,7 @@ func TestPostContainersStart(t *testing.T) {
766 766
 
767 767
 	srv := &Server{runtime: runtime}
768 768
 
769
-	container, err := NewBuilder(runtime).Create(
769
+	container, err := runtime.Create(
770 770
 		&Config{
771 771
 			Image:     GetTestImage(runtime).ID,
772 772
 			Cmd:       []string{"/bin/cat"},
... ...
@@ -816,7 +808,7 @@ func TestPostContainersStop(t *testing.T) {
816 816
 
817 817
 	srv := &Server{runtime: runtime}
818 818
 
819
-	container, err := NewBuilder(runtime).Create(
819
+	container, err := runtime.Create(
820 820
 		&Config{
821 821
 			Image:     GetTestImage(runtime).ID,
822 822
 			Cmd:       []string{"/bin/cat"},
... ...
@@ -863,7 +855,7 @@ func TestPostContainersWait(t *testing.T) {
863 863
 
864 864
 	srv := &Server{runtime: runtime}
865 865
 
866
-	container, err := NewBuilder(runtime).Create(
866
+	container, err := runtime.Create(
867 867
 		&Config{
868 868
 			Image:     GetTestImage(runtime).ID,
869 869
 			Cmd:       []string{"/bin/sleep", "1"},
... ...
@@ -905,7 +897,7 @@ func TestPostContainersAttach(t *testing.T) {
905 905
 
906 906
 	srv := &Server{runtime: runtime}
907 907
 
908
-	container, err := NewBuilder(runtime).Create(
908
+	container, err := runtime.Create(
909 909
 		&Config{
910 910
 			Image:     GetTestImage(runtime).ID,
911 911
 			Cmd:       []string{"/bin/cat"},
... ...
@@ -997,7 +989,7 @@ func TestDeleteContainers(t *testing.T) {
997 997
 
998 998
 	srv := &Server{runtime: runtime}
999 999
 
1000
-	container, err := NewBuilder(runtime).Create(&Config{
1000
+	container, err := runtime.Create(&Config{
1001 1001
 		Image: GetTestImage(runtime).ID,
1002 1002
 		Cmd:   []string{"touch", "/test"},
1003 1003
 	})
... ...
@@ -1184,10 +1176,8 @@ func TestPostContainersCopy(t *testing.T) {
1184 1184
 
1185 1185
 	srv := &Server{runtime: runtime}
1186 1186
 
1187
-	builder := NewBuilder(runtime)
1188
-
1189 1187
 	// Create a container and remove a file
1190
-	container, err := builder.Create(
1188
+	container, err := runtime.Create(
1191 1189
 		&Config{
1192 1190
 			Image: GetTestImage(runtime).ID,
1193 1191
 			Cmd:   []string{"touch", "/test.txt"},
... ...
@@ -26,10 +26,11 @@ var (
26 26
 )
27 27
 
28 28
 type AuthConfig struct {
29
-	Username string `json:"username,omitempty"`
30
-	Password string `json:"password,omitempty"`
31
-	Auth     string `json:"auth"`
32
-	Email    string `json:"email"`
29
+	Username      string `json:"username,omitempty"`
30
+	Password      string `json:"password,omitempty"`
31
+	Auth          string `json:"auth"`
32
+	Email         string `json:"email"`
33
+	ServerAddress string `json:"serveraddress,omitempty"`
33 34
 }
34 35
 
35 36
 type ConfigFile struct {
... ...
@@ -96,6 +97,7 @@ func LoadConfig(rootPath string) (*ConfigFile, error) {
96 96
 		}
97 97
 		origEmail := strings.Split(arr[1], " = ")
98 98
 		authConfig.Email = origEmail[1]
99
+		authConfig.ServerAddress = IndexServerAddress()
99 100
 		configFile.Configs[IndexServerAddress()] = authConfig
100 101
 	} else {
101 102
 		for k, authConfig := range configFile.Configs {
... ...
@@ -105,6 +107,7 @@ func LoadConfig(rootPath string) (*ConfigFile, error) {
105 105
 			}
106 106
 			authConfig.Auth = ""
107 107
 			configFile.Configs[k] = authConfig
108
+			authConfig.ServerAddress = k
108 109
 		}
109 110
 	}
110 111
 	return &configFile, nil
... ...
@@ -125,7 +128,7 @@ func SaveConfig(configFile *ConfigFile) error {
125 125
 		authCopy.Auth = encodeAuth(&authCopy)
126 126
 		authCopy.Username = ""
127 127
 		authCopy.Password = ""
128
-
128
+		authCopy.ServerAddress = ""
129 129
 		configs[k] = authCopy
130 130
 	}
131 131
 
... ...
@@ -146,14 +149,26 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
146 146
 	reqStatusCode := 0
147 147
 	var status string
148 148
 	var reqBody []byte
149
-	jsonBody, err := json.Marshal(authConfig)
149
+
150
+	serverAddress := authConfig.ServerAddress
151
+	if serverAddress == "" {
152
+		serverAddress = IndexServerAddress()
153
+	}
154
+
155
+	loginAgainstOfficialIndex := serverAddress == IndexServerAddress()
156
+
157
+	// to avoid sending the server address to the server it should be removed before marshalled
158
+	authCopy := *authConfig
159
+	authCopy.ServerAddress = ""
160
+
161
+	jsonBody, err := json.Marshal(authCopy)
150 162
 	if err != nil {
151 163
 		return "", fmt.Errorf("Config Error: %s", err)
152 164
 	}
153 165
 
154 166
 	// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
155 167
 	b := strings.NewReader(string(jsonBody))
156
-	req1, err := http.Post(IndexServerAddress()+"users/", "application/json; charset=utf-8", b)
168
+	req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
157 169
 	if err != nil {
158 170
 		return "", fmt.Errorf("Server Error: %s", err)
159 171
 	}
... ...
@@ -165,14 +180,23 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
165 165
 	}
166 166
 
167 167
 	if reqStatusCode == 201 {
168
-		status = "Account created. Please use the confirmation link we sent" +
169
-			" to your e-mail to activate it."
168
+		if loginAgainstOfficialIndex {
169
+			status = "Account created. Please use the confirmation link we sent" +
170
+				" to your e-mail to activate it."
171
+		} else {
172
+			status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it."
173
+		}
170 174
 	} else if reqStatusCode == 403 {
171
-		return "", fmt.Errorf("Login: Your account hasn't been activated. " +
172
-			"Please check your e-mail for a confirmation link.")
175
+		if loginAgainstOfficialIndex {
176
+			return "", fmt.Errorf("Login: Your account hasn't been activated. " +
177
+				"Please check your e-mail for a confirmation link.")
178
+		} else {
179
+			return "", fmt.Errorf("Login: Your account hasn't been activated. " +
180
+				"Please see the documentation of the registry " + serverAddress + " for instructions how to activate it.")
181
+		}
173 182
 	} else if reqStatusCode == 400 {
174 183
 		if string(reqBody) == "\"Username or email already exists\"" {
175
-			req, err := factory.NewRequest("GET", IndexServerAddress()+"users/", nil)
184
+			req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
176 185
 			req.SetBasicAuth(authConfig.Username, authConfig.Password)
177 186
 			resp, err := client.Do(req)
178 187
 			if err != nil {
... ...
@@ -199,3 +223,52 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
199 199
 	}
200 200
 	return status, nil
201 201
 }
202
+
203
+// this method matches a auth configuration to a server address or a url
204
+func (config *ConfigFile) ResolveAuthConfig(registry string) AuthConfig {
205
+	if registry == IndexServerAddress() || len(registry) == 0 {
206
+		// default to the index server
207
+		return config.Configs[IndexServerAddress()]
208
+	}
209
+	// if its not the index server there are three cases:
210
+	//
211
+	// 1. this is a full config url -> it should be used as is
212
+	// 2. it could be a full url, but with the wrong protocol
213
+	// 3. it can be the hostname optionally with a port
214
+	//
215
+	// as there is only one auth entry which is fully qualified we need to start
216
+	// parsing and matching
217
+
218
+	swapProtocoll := func(url string) string {
219
+		if strings.HasPrefix(url, "http:") {
220
+			return strings.Replace(url, "http:", "https:", 1)
221
+		}
222
+		if strings.HasPrefix(url, "https:") {
223
+			return strings.Replace(url, "https:", "http:", 1)
224
+		}
225
+		return url
226
+	}
227
+
228
+	resolveIgnoringProtocol := func(url string) AuthConfig {
229
+		if c, found := config.Configs[url]; found {
230
+			return c
231
+		}
232
+		registrySwappedProtocoll := swapProtocoll(url)
233
+		// now try to match with the different protocol
234
+		if c, found := config.Configs[registrySwappedProtocoll]; found {
235
+			return c
236
+		}
237
+		return AuthConfig{}
238
+	}
239
+
240
+	// match both protocols as it could also be a server name like httpfoo
241
+	if strings.HasPrefix(registry, "http:") || strings.HasPrefix(registry, "https:") {
242
+		return resolveIgnoringProtocol(registry)
243
+	}
244
+
245
+	url := "https://" + registry
246
+	if !strings.Contains(registry, "/") {
247
+		url = url + "/v1/"
248
+	}
249
+	return resolveIgnoringProtocol(url)
250
+}
202 251
deleted file mode 100644
... ...
@@ -1,154 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"fmt"
5
-	"github.com/dotcloud/docker/utils"
6
-	"os"
7
-	"path"
8
-	"time"
9
-)
10
-
11
-var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
12
-
13
-type Builder struct {
14
-	runtime      *Runtime
15
-	repositories *TagStore
16
-	graph        *Graph
17
-
18
-	config *Config
19
-	image  *Image
20
-}
21
-
22
-func NewBuilder(runtime *Runtime) *Builder {
23
-	return &Builder{
24
-		runtime:      runtime,
25
-		graph:        runtime.graph,
26
-		repositories: runtime.repositories,
27
-	}
28
-}
29
-
30
-func (builder *Builder) Create(config *Config) (*Container, error) {
31
-	// Lookup image
32
-	img, err := builder.repositories.LookupImage(config.Image)
33
-	if err != nil {
34
-		return nil, err
35
-	}
36
-
37
-	if img.Config != nil {
38
-		MergeConfig(config, img.Config)
39
-	}
40
-
41
-	if len(config.Entrypoint) != 0 && config.Cmd == nil {
42
-		config.Cmd = []string{}
43
-	} else if config.Cmd == nil || len(config.Cmd) == 0 {
44
-		return nil, fmt.Errorf("No command specified")
45
-	}
46
-
47
-	// Generate id
48
-	id := GenerateID()
49
-	// Generate default hostname
50
-	// FIXME: the lxc template no longer needs to set a default hostname
51
-	if config.Hostname == "" {
52
-		config.Hostname = id[:12]
53
-	}
54
-
55
-	var args []string
56
-	var entrypoint string
57
-
58
-	if len(config.Entrypoint) != 0 {
59
-		entrypoint = config.Entrypoint[0]
60
-		args = append(config.Entrypoint[1:], config.Cmd...)
61
-	} else {
62
-		entrypoint = config.Cmd[0]
63
-		args = config.Cmd[1:]
64
-	}
65
-
66
-	container := &Container{
67
-		// FIXME: we should generate the ID here instead of receiving it as an argument
68
-		ID:              id,
69
-		Created:         time.Now(),
70
-		Path:            entrypoint,
71
-		Args:            args, //FIXME: de-duplicate from config
72
-		Config:          config,
73
-		Image:           img.ID, // Always use the resolved image id
74
-		NetworkSettings: &NetworkSettings{},
75
-		// FIXME: do we need to store this in the container?
76
-		SysInitPath: sysInitPath,
77
-	}
78
-	container.root = builder.runtime.containerRoot(container.ID)
79
-	// Step 1: create the container directory.
80
-	// This doubles as a barrier to avoid race conditions.
81
-	if err := os.Mkdir(container.root, 0700); err != nil {
82
-		return nil, err
83
-	}
84
-
85
-	resolvConf, err := utils.GetResolvConf()
86
-	if err != nil {
87
-		return nil, err
88
-	}
89
-
90
-	if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
91
-		//"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns
92
-		builder.runtime.Dns = defaultDns
93
-	}
94
-
95
-	// If custom dns exists, then create a resolv.conf for the container
96
-	if len(config.Dns) > 0 || len(builder.runtime.Dns) > 0 {
97
-		var dns []string
98
-		if len(config.Dns) > 0 {
99
-			dns = config.Dns
100
-		} else {
101
-			dns = builder.runtime.Dns
102
-		}
103
-		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
104
-		f, err := os.Create(container.ResolvConfPath)
105
-		if err != nil {
106
-			return nil, err
107
-		}
108
-		defer f.Close()
109
-		for _, dns := range dns {
110
-			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
111
-				return nil, err
112
-			}
113
-		}
114
-	} else {
115
-		container.ResolvConfPath = "/etc/resolv.conf"
116
-	}
117
-
118
-	// Step 2: save the container json
119
-	if err := container.ToDisk(); err != nil {
120
-		return nil, err
121
-	}
122
-	// Step 3: register the container
123
-	if err := builder.runtime.Register(container); err != nil {
124
-		return nil, err
125
-	}
126
-	return container, nil
127
-}
128
-
129
-// Commit creates a new filesystem image from the current state of a container.
130
-// The image can optionally be tagged into a repository
131
-func (builder *Builder) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
132
-	// FIXME: freeze the container before copying it to avoid data corruption?
133
-	// FIXME: this shouldn't be in commands.
134
-	if err := container.EnsureMounted(); err != nil {
135
-		return nil, err
136
-	}
137
-
138
-	rwTar, err := container.ExportRw()
139
-	if err != nil {
140
-		return nil, err
141
-	}
142
-	// Create a new image from the container's base layers + a new layer from container changes
143
-	img, err := builder.graph.Create(rwTar, container, comment, author, config)
144
-	if err != nil {
145
-		return nil, err
146
-	}
147
-	// Register the image if needed
148
-	if repository != "" {
149
-		if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil {
150
-			return img, err
151
-		}
152
-	}
153
-	return img, nil
154
-}
... ...
@@ -1,7 +1,6 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	"bufio"
5 4
 	"encoding/json"
6 5
 	"fmt"
7 6
 	"github.com/dotcloud/docker/utils"
... ...
@@ -23,7 +22,6 @@ type BuildFile interface {
23 23
 
24 24
 type buildFile struct {
25 25
 	runtime *Runtime
26
-	builder *Builder
27 26
 	srv     *Server
28 27
 
29 28
 	image        string
... ...
@@ -293,7 +291,7 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error {
293 293
 	}
294 294
 	fi, err := os.Stat(origPath)
295 295
 	if err != nil {
296
-		return err
296
+		return fmt.Errorf("%s: no such file or directory", orig)
297 297
 	}
298 298
 	if fi.IsDir() {
299 299
 		if err := CopyWithTar(origPath, destPath); err != nil {
... ...
@@ -337,7 +335,7 @@ func (b *buildFile) CmdAdd(args string) error {
337 337
 
338 338
 	b.config.Image = b.image
339 339
 	// Create the container and start it
340
-	container, err := b.builder.Create(b.config)
340
+	container, err := b.runtime.Create(b.config)
341 341
 	if err != nil {
342 342
 		return err
343 343
 	}
... ...
@@ -372,7 +370,7 @@ func (b *buildFile) run() (string, error) {
372 372
 	b.config.Image = b.image
373 373
 
374 374
 	// Create the container and start it
375
-	c, err := b.builder.Create(b.config)
375
+	c, err := b.runtime.Create(b.config)
376 376
 	if err != nil {
377 377
 		return "", err
378 378
 	}
... ...
@@ -428,7 +426,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
428 428
 			}
429 429
 		}
430 430
 
431
-		container, err := b.builder.Create(b.config)
431
+		container, err := b.runtime.Create(b.config)
432 432
 		if err != nil {
433 433
 			return err
434 434
 		}
... ...
@@ -450,7 +448,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
450 450
 	autoConfig := *b.config
451 451
 	autoConfig.Cmd = autoCmd
452 452
 	// Commit the container
453
-	image, err := b.builder.Commit(container, "", "", "", b.maintainer, &autoConfig)
453
+	image, err := b.runtime.Commit(container, "", "", "", b.maintainer, &autoConfig)
454 454
 	if err != nil {
455 455
 		return err
456 456
 	}
... ...
@@ -459,6 +457,9 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
459 459
 	return nil
460 460
 }
461 461
 
462
+// Long lines can be split with a backslash
463
+var lineContinuation = regexp.MustCompile(`\s*\\\s*\n`)
464
+
462 465
 func (b *buildFile) Build(context io.Reader) (string, error) {
463 466
 	// FIXME: @creack any reason for using /tmp instead of ""?
464 467
 	// FIXME: @creack "name" is a terrible variable name
... ...
@@ -471,22 +472,18 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
471 471
 	}
472 472
 	defer os.RemoveAll(name)
473 473
 	b.context = name
474
-	dockerfile, err := os.Open(path.Join(name, "Dockerfile"))
475
-	if err != nil {
474
+	filename := path.Join(name, "Dockerfile")
475
+	if _, err := os.Stat(filename); os.IsNotExist(err) {
476 476
 		return "", fmt.Errorf("Can't build a directory with no Dockerfile")
477 477
 	}
478
-	// FIXME: "file" is also a terrible variable name ;)
479
-	file := bufio.NewReader(dockerfile)
478
+	fileBytes, err := ioutil.ReadFile(filename)
479
+	if err != nil {
480
+		return "", err
481
+	}
482
+	dockerfile := string(fileBytes)
483
+	dockerfile = lineContinuation.ReplaceAllString(dockerfile, " ")
480 484
 	stepN := 0
481
-	for {
482
-		line, err := file.ReadString('\n')
483
-		if err != nil {
484
-			if err == io.EOF && line == "" {
485
-				break
486
-			} else if err != io.EOF {
487
-				return "", err
488
-			}
489
-		}
485
+	for _, line := range strings.Split(dockerfile, "\n") {
490 486
 		line = strings.Trim(strings.Replace(line, "\t", " ", -1), " \t\r\n")
491 487
 		// Skip comments and empty line
492 488
 		if len(line) == 0 || line[0] == '#' {
... ...
@@ -524,7 +521,6 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
524 524
 
525 525
 func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache bool) BuildFile {
526 526
 	return &buildFile{
527
-		builder:       NewBuilder(srv.runtime),
528 527
 		runtime:       srv.runtime,
529 528
 		srv:           srv,
530 529
 		config:        &Config{},
... ...
@@ -45,6 +45,34 @@ run    [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
45 45
 		nil,
46 46
 	},
47 47
 
48
+	// Exactly the same as above, except uses a line split with a \ to test
49
+	// multiline support.
50
+	{
51
+		`
52
+from   {IMAGE}
53
+run    sh -c 'echo root:testpass \
54
+	> /tmp/passwd'
55
+run    mkdir -p /var/run/sshd
56
+run    [ "$(cat /tmp/passwd)" = "root:testpass" ]
57
+run    [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
58
+`,
59
+		nil,
60
+		nil,
61
+	},
62
+
63
+	// Line containing literal "\n"
64
+	{
65
+		`
66
+from   {IMAGE}
67
+run    sh -c 'echo root:testpass > /tmp/passwd'
68
+run    echo "foo \n bar"; echo "baz"
69
+run    mkdir -p /var/run/sshd
70
+run    [ "$(cat /tmp/passwd)" = "root:testpass" ]
71
+run    [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
72
+`,
73
+		nil,
74
+		nil,
75
+	},
48 76
 	{
49 77
 		`
50 78
 from {IMAGE}
... ...
@@ -483,3 +511,51 @@ func TestForbiddenContextPath(t *testing.T) {
483 483
 		t.Fail()
484 484
 	}
485 485
 }
486
+
487
+func TestBuildADDFileNotFound(t *testing.T) {
488
+	runtime, err := newTestRuntime()
489
+	if err != nil {
490
+		t.Fatal(err)
491
+	}
492
+	defer nuke(runtime)
493
+
494
+	srv := &Server{
495
+		runtime:     runtime,
496
+		pullingPool: make(map[string]struct{}),
497
+		pushingPool: make(map[string]struct{}),
498
+	}
499
+
500
+	context := testContextTemplate{`
501
+        from {IMAGE}
502
+        add foo /usr/local/bar
503
+        `,
504
+		nil, nil}
505
+
506
+	httpServer, err := mkTestingFileServer(context.remoteFiles)
507
+	if err != nil {
508
+		t.Fatal(err)
509
+	}
510
+	defer httpServer.Close()
511
+
512
+	idx := strings.LastIndex(httpServer.URL, ":")
513
+	if idx < 0 {
514
+		t.Fatalf("could not get port from test http server address %s", httpServer.URL)
515
+	}
516
+	port := httpServer.URL[idx+1:]
517
+
518
+	ip := srv.runtime.networkManager.bridgeNetwork.IP
519
+	dockerfile := constructDockerfile(context.dockerfile, ip, port)
520
+
521
+	buildfile := NewBuildFile(srv, ioutil.Discard, false, true)
522
+	_, err = buildfile.Build(mkTestContext(dockerfile, context.files, t))
523
+
524
+	if err == nil {
525
+		t.Log("Error should not be nil")
526
+		t.Fail()
527
+	}
528
+
529
+	if err.Error() != "foo: no such file or directory" {
530
+		t.Logf("Error message is not expected: %s", err.Error())
531
+		t.Fail()
532
+	}
533
+}
... ...
@@ -99,7 +99,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
99 99
 		changes = append(changes, change)
100 100
 		return nil
101 101
 	})
102
-	if err != nil {
102
+	if err != nil && !os.IsNotExist(err) {
103 103
 		return nil, err
104 104
 	}
105 105
 	return changes, nil
... ...
@@ -2,11 +2,14 @@ package docker
2 2
 
3 3
 import (
4 4
 	"archive/tar"
5
+	"bufio"
5 6
 	"bytes"
7
+	"encoding/base64"
6 8
 	"encoding/json"
7 9
 	"flag"
8 10
 	"fmt"
9 11
 	"github.com/dotcloud/docker/auth"
12
+	"github.com/dotcloud/docker/registry"
10 13
 	"github.com/dotcloud/docker/term"
11 14
 	"github.com/dotcloud/docker/utils"
12 15
 	"io"
... ...
@@ -20,17 +23,17 @@ import (
20 20
 	"path/filepath"
21 21
 	"reflect"
22 22
 	"runtime"
23
+	"sort"
23 24
 	"strconv"
24 25
 	"strings"
25 26
 	"syscall"
26 27
 	"text/tabwriter"
27 28
 	"time"
28
-	"unicode"
29 29
 )
30 30
 
31 31
 var (
32 32
 	GITCOMMIT string
33
-	VERSION string
33
+	VERSION   string
34 34
 )
35 35
 
36 36
 func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
... ...
@@ -126,7 +129,7 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
126 126
 	v.Set("url", cmd.Arg(1))
127 127
 	v.Set("path", cmd.Arg(2))
128 128
 
129
-	if err := cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, cli.out); err != nil {
129
+	if err := cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, cli.out, nil); err != nil {
130 130
 		return err
131 131
 	}
132 132
 	return nil
... ...
@@ -252,75 +255,27 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
252 252
 
253 253
 // 'docker login': login / register a user to registry service.
254 254
 func (cli *DockerCli) CmdLogin(args ...string) error {
255
-	var readStringOnRawTerminal = func(stdin io.Reader, stdout io.Writer, echo bool) string {
256
-		char := make([]byte, 1)
257
-		buffer := make([]byte, 64)
258
-		var i = 0
259
-		for i < len(buffer) {
260
-			n, err := stdin.Read(char)
261
-			if n > 0 {
262
-				if char[0] == '\r' || char[0] == '\n' {
263
-					stdout.Write([]byte{'\r', '\n'})
264
-					break
265
-				} else if char[0] == 127 || char[0] == '\b' {
266
-					if i > 0 {
267
-						if echo {
268
-							stdout.Write([]byte{'\b', ' ', '\b'})
269
-						}
270
-						i--
271
-					}
272
-				} else if !unicode.IsSpace(rune(char[0])) &&
273
-					!unicode.IsControl(rune(char[0])) {
274
-					if echo {
275
-						stdout.Write(char)
276
-					}
277
-					buffer[i] = char[0]
278
-					i++
279
-				}
280
-			}
281
-			if err != nil {
282
-				if err != io.EOF {
283
-					fmt.Fprintf(stdout, "Read error: %v\r\n", err)
284
-				}
285
-				break
286
-			}
287
-		}
288
-		return string(buffer[:i])
289
-	}
290
-	var readAndEchoString = func(stdin io.Reader, stdout io.Writer) string {
291
-		return readStringOnRawTerminal(stdin, stdout, true)
292
-	}
293
-	var readString = func(stdin io.Reader, stdout io.Writer) string {
294
-		return readStringOnRawTerminal(stdin, stdout, false)
295
-	}
255
+	cmd := Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+auth.IndexServerAddress()+"\" is the default.")
296 256
 
297
-	cmd := Subcmd("login", "[OPTIONS]", "Register or Login to the docker registry server")
298
-	flUsername := cmd.String("u", "", "username")
299
-	flPassword := cmd.String("p", "", "password")
300
-	flEmail := cmd.String("e", "", "email")
257
+	var username, password, email string
258
+
259
+	cmd.StringVar(&username, "u", "", "username")
260
+	cmd.StringVar(&password, "p", "", "password")
261
+	cmd.StringVar(&email, "e", "", "email")
301 262
 	err := cmd.Parse(args)
302 263
 	if err != nil {
303 264
 		return nil
304 265
 	}
305
-
306
-	cli.LoadConfigFile()
307
-
308
-	var oldState *term.State
309
-	if *flUsername == "" || *flPassword == "" || *flEmail == "" {
310
-		oldState, err = term.SetRawTerminal(cli.terminalFd)
266
+	serverAddress := auth.IndexServerAddress()
267
+	if len(cmd.Args()) > 0 {
268
+		serverAddress, err = registry.ExpandAndVerifyRegistryUrl(cmd.Arg(0))
311 269
 		if err != nil {
312 270
 			return err
313 271
 		}
314
-		defer term.RestoreTerminal(cli.terminalFd, oldState)
272
+		fmt.Fprintf(cli.out, "Login against server at %s\n", serverAddress)
315 273
 	}
316 274
 
317
-	var (
318
-		username string
319
-		password string
320
-		email    string
321
-	)
322
-
323
-	var promptDefault = func(prompt string, configDefault string) {
275
+	promptDefault := func(prompt string, configDefault string) {
324 276
 		if configDefault == "" {
325 277
 			fmt.Fprintf(cli.out, "%s: ", prompt)
326 278
 		} else {
... ...
@@ -328,55 +283,64 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
328 328
 		}
329 329
 	}
330 330
 
331
+	readInput := func(in io.Reader, out io.Writer) string {
332
+		reader := bufio.NewReader(in)
333
+		line, _, err := reader.ReadLine()
334
+		if err != nil {
335
+			fmt.Fprintln(out, err.Error())
336
+			os.Exit(1)
337
+		}
338
+		return string(line)
339
+	}
340
+
341
+	cli.LoadConfigFile()
331 342
 	authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()]
332 343
 	if !ok {
333 344
 		authconfig = auth.AuthConfig{}
334 345
 	}
335 346
 
336
-	if *flUsername == "" {
347
+	if username == "" {
337 348
 		promptDefault("Username", authconfig.Username)
338
-		username = readAndEchoString(cli.in, cli.out)
349
+		username = readInput(cli.in, cli.out)
339 350
 		if username == "" {
340 351
 			username = authconfig.Username
341 352
 		}
342
-	} else {
343
-		username = *flUsername
344 353
 	}
345 354
 	if username != authconfig.Username {
346
-		if *flPassword == "" {
355
+		if password == "" {
356
+			oldState, _ := term.SaveState(cli.terminalFd)
347 357
 			fmt.Fprintf(cli.out, "Password: ")
348
-			password = readString(cli.in, cli.out)
358
+			term.DisableEcho(cli.terminalFd, oldState)
359
+
360
+			password = readInput(cli.in, cli.out)
361
+			fmt.Fprint(cli.out, "\n")
362
+
363
+			term.RestoreTerminal(cli.terminalFd, oldState)
349 364
 			if password == "" {
350 365
 				return fmt.Errorf("Error : Password Required")
351 366
 			}
352
-		} else {
353
-			password = *flPassword
354 367
 		}
355 368
 
356
-		if *flEmail == "" {
369
+		if email == "" {
357 370
 			promptDefault("Email", authconfig.Email)
358
-			email = readAndEchoString(cli.in, cli.out)
371
+			email = readInput(cli.in, cli.out)
359 372
 			if email == "" {
360 373
 				email = authconfig.Email
361 374
 			}
362
-		} else {
363
-			email = *flEmail
364 375
 		}
365 376
 	} else {
366 377
 		password = authconfig.Password
367 378
 		email = authconfig.Email
368 379
 	}
369
-	if oldState != nil {
370
-		term.RestoreTerminal(cli.terminalFd, oldState)
371
-	}
372 380
 	authconfig.Username = username
373 381
 	authconfig.Password = password
374 382
 	authconfig.Email = email
375
-	cli.configFile.Configs[auth.IndexServerAddress()] = authconfig
383
+	authconfig.ServerAddress = serverAddress
384
+	cli.configFile.Configs[serverAddress] = authconfig
376 385
 
377
-	body, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[auth.IndexServerAddress()])
386
+	body, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress])
378 387
 	if statusCode == 401 {
379
-		delete(cli.configFile.Configs, auth.IndexServerAddress())
388
+		delete(cli.configFile.Configs, serverAddress)
380 389
 		auth.SaveConfig(cli.configFile)
381 390
 		return err
382 391
 	}
... ...
@@ -408,16 +372,11 @@ func (cli *DockerCli) CmdWait(args ...string) error {
408 408
 		return nil
409 409
 	}
410 410
 	for _, name := range cmd.Args() {
411
-		body, _, err := cli.call("POST", "/containers/"+name+"/wait", nil)
411
+		status, err := waitForExit(cli, name)
412 412
 		if err != nil {
413 413
 			fmt.Fprintf(cli.err, "%s", err)
414 414
 		} else {
415
-			var out APIWait
416
-			err = json.Unmarshal(body, &out)
417
-			if err != nil {
418
-				return err
419
-			}
420
-			fmt.Fprintf(cli.out, "%d\n", out.StatusCode)
415
+			fmt.Fprintf(cli.out, "%d\n", status)
421 416
 		}
422 417
 	}
423 418
 	return nil
... ...
@@ -434,8 +393,9 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
434 434
 		cmd.Usage()
435 435
 		return nil
436 436
 	}
437
-
438
-	fmt.Fprintf(cli.out, "Client version: %s\n", VERSION)
437
+	if VERSION != "" {
438
+		fmt.Fprintf(cli.out, "Client version: %s\n", VERSION)
439
+	}
439 440
 	fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version())
440 441
 	if GITCOMMIT != "" {
441 442
 		fmt.Fprintf(cli.out, "Git commit (client): %s\n", GITCOMMIT)
... ...
@@ -452,7 +412,9 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
452 452
 		utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
453 453
 		return err
454 454
 	}
455
-	fmt.Fprintf(cli.out, "Server version: %s\n", out.Version)
455
+	if out.Version != "" {
456
+		fmt.Fprintf(cli.out, "Server version: %s\n", out.Version)
457
+	}
456 458
 	if out.GitCommit != "" {
457 459
 		fmt.Fprintf(cli.out, "Git commit (server): %s\n", out.GitCommit)
458 460
 	}
... ...
@@ -463,7 +425,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
463 463
 	release := utils.GetReleaseVersion()
464 464
 	if release != "" {
465 465
 		fmt.Fprintf(cli.out, "Last stable version: %s", release)
466
-		if strings.Trim(VERSION, "-dev") != release || strings.Trim(out.Version, "-dev") != release {
466
+		if (VERSION != "" || out.Version != "") && (strings.Trim(VERSION, "-dev") != release || strings.Trim(out.Version, "-dev") != release) {
467 467
 			fmt.Fprintf(cli.out, ", please update docker")
468 468
 		}
469 469
 		fmt.Fprintf(cli.out, "\n")
... ...
@@ -615,7 +577,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
615 615
 		if err != nil {
616 616
 			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
617 617
 			if err != nil {
618
-				fmt.Fprintf(cli.err, "%s\n", err)
618
+				fmt.Fprintf(cli.err, "No such image or container: %s\n", name)
619 619
 				continue
620 620
 			}
621 621
 		}
... ...
@@ -829,7 +791,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
829 829
 	v.Set("tag", tag)
830 830
 	v.Set("fromSrc", src)
831 831
 
832
-	err := cli.stream("POST", "/images/create?"+v.Encode(), cli.in, cli.out)
832
+	err := cli.stream("POST", "/images/create?"+v.Encode(), cli.in, cli.out, nil)
833 833
 	if err != nil {
834 834
 		return err
835 835
 	}
... ...
@@ -850,6 +812,13 @@ func (cli *DockerCli) CmdPush(args ...string) error {
850 850
 
851 851
 	cli.LoadConfigFile()
852 852
 
853
+	// Resolve the Repository name from fqn to endpoint + name
854
+	endpoint, _, err := registry.ResolveRepositoryName(name)
855
+	if err != nil {
856
+		return err
857
+	}
858
+	// Resolve the Auth config relevant for this server
859
+	authConfig := cli.configFile.ResolveAuthConfig(endpoint)
853 860
 	// If we're not using a custom registry, we know the restrictions
854 861
 	// applied to repository names and can warn the user in advance.
855 862
 	// Custom repositories can have different rules, and we must also
... ...
@@ -863,22 +832,28 @@ func (cli *DockerCli) CmdPush(args ...string) error {
863 863
 	}
864 864
 
865 865
 	v := url.Values{}
866
-	push := func() error {
867
-		buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()])
866
+	push := func(authConfig auth.AuthConfig) error {
867
+		buf, err := json.Marshal(authConfig)
868 868
 		if err != nil {
869 869
 			return err
870 870
 		}
871
+		registryAuthHeader := []string{
872
+			base64.URLEncoding.EncodeToString(buf),
873
+		}
871 874
 
872
-		return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), cli.out)
875
+		return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
876
+			"X-Registry-Auth": registryAuthHeader,
877
+		})
873 878
 	}
874 879
 
875
-	if err := push(); err != nil {
876
-		if err.Error() == "Authentication is required." {
880
+	if err := push(authConfig); err != nil {
881
+		if err.Error() == registry.ErrLoginRequired.Error() {
877 882
 			fmt.Fprintln(cli.out, "\nPlease login prior to push:")
878
-			if err := cli.CmdLogin(""); err != nil {
883
+			if err := cli.CmdLogin(endpoint); err != nil {
879 884
 				return err
880 885
 			}
881
-			return push()
886
+			authConfig := cli.configFile.ResolveAuthConfig(endpoint)
887
+			return push(authConfig)
882 888
 		}
883 889
 		return err
884 890
 	}
... ...
@@ -902,11 +877,43 @@ func (cli *DockerCli) CmdPull(args ...string) error {
902 902
 		*tag = parsedTag
903 903
 	}
904 904
 
905
+	// Resolve the Repository name from fqn to endpoint + name
906
+	endpoint, _, err := registry.ResolveRepositoryName(remote)
907
+	if err != nil {
908
+		return err
909
+	}
910
+
911
+	cli.LoadConfigFile()
912
+
913
+	// Resolve the Auth config relevant for this server
914
+	authConfig := cli.configFile.ResolveAuthConfig(endpoint)
905 915
 	v := url.Values{}
906 916
 	v.Set("fromImage", remote)
907 917
 	v.Set("tag", *tag)
908 918
 
909
-	if err := cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out); err != nil {
919
+	pull := func(authConfig auth.AuthConfig) error {
920
+		buf, err := json.Marshal(authConfig)
921
+		if err != nil {
922
+			return err
923
+		}
924
+		registryAuthHeader := []string{
925
+			base64.URLEncoding.EncodeToString(buf),
926
+		}
927
+
928
+		return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
929
+			"X-Registry-Auth": registryAuthHeader,
930
+		})
931
+	}
932
+
933
+	if err := pull(authConfig); err != nil {
934
+		if err.Error() == registry.ErrLoginRequired.Error() {
935
+			fmt.Fprintln(cli.out, "\nPlease login prior to push:")
936
+			if err := cli.CmdLogin(endpoint); err != nil {
937
+				return err
938
+			}
939
+			authConfig := cli.configFile.ResolveAuthConfig(endpoint)
940
+			return pull(authConfig)
941
+		}
910 942
 		return err
911 943
 	}
912 944
 
... ...
@@ -996,6 +1003,19 @@ func (cli *DockerCli) CmdImages(args ...string) error {
996 996
 	return nil
997 997
 }
998 998
 
999
+func displayablePorts(ports []APIPort) string {
1000
+	result := []string{}
1001
+	for _, port := range ports {
1002
+		if port.Type == "tcp" {
1003
+			result = append(result, fmt.Sprintf("%d->%d", port.PublicPort, port.PrivatePort))
1004
+		} else {
1005
+			result = append(result, fmt.Sprintf("%d->%d/%s", port.PublicPort, port.PrivatePort, port.Type))
1006
+		}
1007
+	}
1008
+	sort.Strings(result)
1009
+	return strings.Join(result, ", ")
1010
+}
1011
+
999 1012
 func (cli *DockerCli) CmdPs(args ...string) error {
1000 1013
 	cmd := Subcmd("ps", "[OPTIONS]", "List containers")
1001 1014
 	quiet := cmd.Bool("q", false, "Only display numeric IDs")
... ...
@@ -1053,9 +1073,9 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1053 1053
 	for _, out := range outs {
1054 1054
 		if !*quiet {
1055 1055
 			if *noTrunc {
1056
-				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)
1056
+				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))
1057 1057
 			} else {
1058
-				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)
1058
+				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))
1059 1059
 			}
1060 1060
 			if *size {
1061 1061
 				if out.SizeRootFs > 0 {
... ...
@@ -1140,7 +1160,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
1140 1140
 		v.Set("since", *since)
1141 1141
 	}
1142 1142
 
1143
-	if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out); err != nil {
1143
+	if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
1144 1144
 		return err
1145 1145
 	}
1146 1146
 	return nil
... ...
@@ -1157,7 +1177,7 @@ func (cli *DockerCli) CmdExport(args ...string) error {
1157 1157
 		return nil
1158 1158
 	}
1159 1159
 
1160
-	if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out); err != nil {
1160
+	if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil {
1161 1161
 		return err
1162 1162
 	}
1163 1163
 	return nil
... ...
@@ -1423,13 +1443,36 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1423 1423
 			tag = DEFAULTTAG
1424 1424
 		}
1425 1425
 
1426
-		fmt.Printf("Unable to find image '%s' (tag: %s) locally\n", config.Image, tag)
1426
+		fmt.Fprintf(cli.err, "Unable to find image '%s' (tag: %s) locally\n", config.Image, tag)
1427 1427
 
1428 1428
 		v := url.Values{}
1429 1429
 		repos, tag := utils.ParseRepositoryTag(config.Image)
1430 1430
 		v.Set("fromImage", repos)
1431 1431
 		v.Set("tag", tag)
1432
-		err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err)
1432
+
1433
+		// Resolve the Repository name from fqn to endpoint + name
1434
+		var endpoint string
1435
+		endpoint, _, err = registry.ResolveRepositoryName(repos)
1436
+		if err != nil {
1437
+			return err
1438
+		}
1439
+
1440
+		// Load the auth config file, to be able to pull the image
1441
+		cli.LoadConfigFile()
1442
+
1443
+		// Resolve the Auth config relevant for this server
1444
+		authConfig := cli.configFile.ResolveAuthConfig(endpoint)
1445
+		buf, err := json.Marshal(authConfig)
1446
+		if err != nil {
1447
+			return err
1448
+		}
1449
+
1450
+		registryAuthHeader := []string{
1451
+			base64.URLEncoding.EncodeToString(buf),
1452
+		}
1453
+		err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{
1454
+			"X-Registry-Auth": registryAuthHeader,
1455
+		})
1433 1456
 		if err != nil {
1434 1457
 			return err
1435 1458
 		}
... ...
@@ -1515,8 +1558,18 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1515 1515
 	}
1516 1516
 
1517 1517
 	if !config.AttachStdout && !config.AttachStderr {
1518
+		// Detached mode
1518 1519
 		<-wait
1520
+	} else {
1521
+		status, err := waitForExit(cli, runResult.ID)
1522
+		if err != nil {
1523
+			return err
1524
+		}
1525
+		if status != 0 {
1526
+			return &utils.StatusError{status}
1527
+		}
1519 1528
 	}
1529
+
1520 1530
 	return nil
1521 1531
 }
1522 1532
 
... ...
@@ -1606,7 +1659,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
1606 1606
 	return body, resp.StatusCode, nil
1607 1607
 }
1608 1608
 
1609
-func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) error {
1609
+func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
1610 1610
 	if (method == "POST" || method == "PUT") && in == nil {
1611 1611
 		in = bytes.NewReader([]byte{})
1612 1612
 	}
... ...
@@ -1619,6 +1672,13 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1619 1619
 	if method == "POST" {
1620 1620
 		req.Header.Set("Content-Type", "plain/text")
1621 1621
 	}
1622
+
1623
+	if headers != nil {
1624
+		for k, v := range headers {
1625
+			req.Header[k] = v
1626
+		}
1627
+	}
1628
+
1622 1629
 	dial, err := net.Dial(cli.proto, cli.addr)
1623 1630
 	if err != nil {
1624 1631
 		if strings.Contains(err.Error(), "connection refused") {
... ...
@@ -1797,6 +1857,19 @@ func (cli *DockerCli) LoadConfigFile() (err error) {
1797 1797
 	return err
1798 1798
 }
1799 1799
 
1800
+func waitForExit(cli *DockerCli, containerId string) (int, error) {
1801
+	body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil)
1802
+	if err != nil {
1803
+		return -1, err
1804
+	}
1805
+
1806
+	var out APIWait
1807
+	if err := json.Unmarshal(body, &out); err != nil {
1808
+		return -1, err
1809
+	}
1810
+	return out.StatusCode, nil
1811
+}
1812
+
1800 1813
 func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
1801 1814
 	var (
1802 1815
 		isTerminal = false
... ...
@@ -152,7 +152,6 @@ func TestRunWorkdirExists(t *testing.T) {
152 152
 
153 153
 }
154 154
 
155
-
156 155
 func TestRunExit(t *testing.T) {
157 156
 	stdin, stdinPipe := io.Pipe()
158 157
 	stdout, stdoutPipe := io.Pipe()
... ...
@@ -11,16 +11,15 @@ import (
11 11
 	"io"
12 12
 	"io/ioutil"
13 13
 	"log"
14
+	"net"
14 15
 	"os"
15 16
 	"os/exec"
16 17
 	"path"
17 18
 	"path/filepath"
18
-	"sort"
19 19
 	"strconv"
20 20
 	"strings"
21 21
 	"syscall"
22 22
 	"time"
23
-	"net"
24 23
 )
25 24
 
26 25
 type Container struct {
... ...
@@ -42,6 +41,8 @@ type Container struct {
42 42
 
43 43
 	SysInitPath    string
44 44
 	ResolvConfPath string
45
+	HostnamePath   string
46
+	HostsPath      string
45 47
 
46 48
 	cmd       *exec.Cmd
47 49
 	stdout    *utils.WriteBroadcaster
... ...
@@ -61,6 +62,7 @@ type Container struct {
61 61
 
62 62
 type Config struct {
63 63
 	Hostname        string
64
+	Domainname      string
64 65
 	User            string
65 66
 	Memory          int64 // Memory limit (in bytes)
66 67
 	MemorySwap      int64 // Total memory usage (memory + swap); set `-1' to disable swap
... ...
@@ -107,7 +109,7 @@ type KeyValuePair struct {
107 107
 
108 108
 func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
109 109
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
110
-	if len(args) > 0 && args[0] != "--help" {
110
+	if os.Getenv("TEST") != "" {
111 111
 		cmd.SetOutput(ioutil.Discard)
112 112
 		cmd.Usage = nil
113 113
 	}
... ...
@@ -203,8 +205,17 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
203 203
 		return nil, nil, cmd, err
204 204
 	}
205 205
 
206
+	hostname := *flHostname
207
+	domainname := ""
208
+
209
+	parts := strings.SplitN(hostname, ".", 2)
210
+	if len(parts) > 1 {
211
+		hostname = parts[0]
212
+		domainname = parts[1]
213
+	}
206 214
 	config := &Config{
207
-		Hostname:        *flHostname,
215
+		Hostname:        hostname,
216
+		Domainname:      domainname,
208 217
 		PortSpecs:       flPorts,
209 218
 		User:            *flUser,
210 219
 		Tty:             *flTty,
... ...
@@ -253,17 +264,28 @@ type NetworkSettings struct {
253 253
 	PortMapping map[string]PortMapping
254 254
 }
255 255
 
256
-// String returns a human-readable description of the port mapping defined in the settings
257
-func (settings *NetworkSettings) PortMappingHuman() string {
258
-	var mapping []string
256
+// returns a more easy to process description of the port mapping defined in the settings
257
+func (settings *NetworkSettings) PortMappingAPI() []APIPort {
258
+	var mapping []APIPort
259 259
 	for private, public := range settings.PortMapping["Tcp"] {
260
-		mapping = append(mapping, fmt.Sprintf("%s->%s", public, private))
260
+		pubint, _ := strconv.ParseInt(public, 0, 0)
261
+		privint, _ := strconv.ParseInt(private, 0, 0)
262
+		mapping = append(mapping, APIPort{
263
+			PrivatePort: privint,
264
+			PublicPort:  pubint,
265
+			Type:        "tcp",
266
+		})
261 267
 	}
262 268
 	for private, public := range settings.PortMapping["Udp"] {
263
-		mapping = append(mapping, fmt.Sprintf("%s->%s/udp", public, private))
269
+		pubint, _ := strconv.ParseInt(public, 0, 0)
270
+		privint, _ := strconv.ParseInt(private, 0, 0)
271
+		mapping = append(mapping, APIPort{
272
+			PrivatePort: privint,
273
+			PublicPort:  pubint,
274
+			Type:        "udp",
275
+		})
264 276
 	}
265
-	sort.Strings(mapping)
266
-	return strings.Join(mapping, ", ")
277
+	return mapping
267 278
 }
268 279
 
269 280
 // Inject the io.Reader at the given path. Note: do not close the reader
... ...
@@ -642,11 +664,13 @@ func (container *Container) Start(hostConfig *HostConfig) error {
642 642
 		if _, exists := container.Volumes[volPath]; exists {
643 643
 			continue
644 644
 		}
645
+		var srcPath string
646
+		srcRW := false
645 647
 		// If an external bind is defined for this volume, use that as a source
646 648
 		if bindMap, exists := binds[volPath]; exists {
647
-			container.Volumes[volPath] = bindMap.SrcPath
649
+			srcPath = bindMap.SrcPath
648 650
 			if strings.ToLower(bindMap.Mode) == "rw" {
649
-				container.VolumesRW[volPath] = true
651
+				srcRW = true
650 652
 			}
651 653
 			// Otherwise create an directory in $ROOT/volumes/ and use that
652 654
 		} else {
... ...
@@ -654,17 +678,36 @@ func (container *Container) Start(hostConfig *HostConfig) error {
654 654
 			if err != nil {
655 655
 				return err
656 656
 			}
657
-			srcPath, err := c.layer()
657
+			srcPath, err = c.layer()
658 658
 			if err != nil {
659 659
 				return err
660 660
 			}
661
-			container.Volumes[volPath] = srcPath
662
-			container.VolumesRW[volPath] = true // RW by default
661
+			srcRW = true // RW by default
663 662
 		}
663
+		container.Volumes[volPath] = srcPath
664
+		container.VolumesRW[volPath] = srcRW
664 665
 		// Create the mountpoint
665
-		if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
666
+		rootVolPath := path.Join(container.RootfsPath(), volPath)
667
+		if err := os.MkdirAll(rootVolPath, 0755); err != nil {
666 668
 			return nil
667 669
 		}
670
+		if srcRW {
671
+			volList, err := ioutil.ReadDir(rootVolPath)
672
+			if err != nil {
673
+				return err
674
+			}
675
+			if len(volList) > 0 {
676
+				srcList, err := ioutil.ReadDir(srcPath)
677
+				if err != nil {
678
+					return err
679
+				}
680
+				if len(srcList) == 0 {
681
+					if err := CopyWithTar(rootVolPath, srcPath); err != nil {
682
+						return err
683
+					}
684
+				}
685
+			}
686
+		}
668 687
 	}
669 688
 
670 689
 	if err := container.generateLXCConfig(hostConfig); err != nil {
... ...
@@ -813,10 +856,10 @@ func (container *Container) allocateNetwork() error {
813 813
 			iface = &NetworkInterface{disabled: true}
814 814
 		} else {
815 815
 			iface = &NetworkInterface{
816
-				IPNet: net.IPNet{IP: net.ParseIP(container.NetworkSettings.IPAddress), Mask: manager.bridgeNetwork.Mask},
816
+				IPNet:   net.IPNet{IP: net.ParseIP(container.NetworkSettings.IPAddress), Mask: manager.bridgeNetwork.Mask},
817 817
 				Gateway: manager.bridgeNetwork.IP,
818 818
 				manager: manager,
819
-				}
819
+			}
820 820
 			ipNum := ipToInt(iface.IPNet.IP)
821 821
 			manager.ipAllocator.inUse[ipNum] = struct{}{}
822 822
 		}
... ...
@@ -827,10 +870,10 @@ func (container *Container) allocateNetwork() error {
827 827
 		portSpecs = container.Config.PortSpecs
828 828
 	} else {
829 829
 		for backend, frontend := range container.NetworkSettings.PortMapping["Tcp"] {
830
-			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/tcp",frontend, backend))
830
+			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/tcp", frontend, backend))
831 831
 		}
832 832
 		for backend, frontend := range container.NetworkSettings.PortMapping["Udp"] {
833
-			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/udp",frontend, backend))
833
+			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/udp", frontend, backend))
834 834
 		}
835 835
 	}
836 836
 
... ...
@@ -18,7 +18,7 @@ import (
18 18
 func TestIDFormat(t *testing.T) {
19 19
 	runtime := mkRuntime(t)
20 20
 	defer nuke(runtime)
21
-	container1, err := NewBuilder(runtime).Create(
21
+	container1, err := runtime.Create(
22 22
 		&Config{
23 23
 			Image: GetTestImage(runtime).ID,
24 24
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
... ...
@@ -138,12 +138,21 @@ func TestDiff(t *testing.T) {
138 138
 	container1, _, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t)
139 139
 	defer runtime.Destroy(container1)
140 140
 
141
+	// The changelog should be empty and not fail before run. See #1705
142
+	c, err := container1.Changes()
143
+	if err != nil {
144
+		t.Fatal(err)
145
+	}
146
+	if len(c) != 0 {
147
+		t.Fatalf("Changelog should be empty before run")
148
+	}
149
+
141 150
 	if err := container1.Run(); err != nil {
142 151
 		t.Fatal(err)
143 152
 	}
144 153
 
145 154
 	// Check the changelog
146
-	c, err := container1.Changes()
155
+	c, err = container1.Changes()
147 156
 	if err != nil {
148 157
 		t.Fatal(err)
149 158
 	}
... ...
@@ -379,7 +388,7 @@ func TestRun(t *testing.T) {
379 379
 func TestOutput(t *testing.T) {
380 380
 	runtime := mkRuntime(t)
381 381
 	defer nuke(runtime)
382
-	container, err := NewBuilder(runtime).Create(
382
+	container, err := runtime.Create(
383 383
 		&Config{
384 384
 			Image: GetTestImage(runtime).ID,
385 385
 			Cmd:   []string{"echo", "-n", "foobar"},
... ...
@@ -402,7 +411,7 @@ func TestKillDifferentUser(t *testing.T) {
402 402
 	runtime := mkRuntime(t)
403 403
 	defer nuke(runtime)
404 404
 
405
-	container, err := NewBuilder(runtime).Create(&Config{
405
+	container, err := runtime.Create(&Config{
406 406
 		Image:     GetTestImage(runtime).ID,
407 407
 		Cmd:       []string{"cat"},
408 408
 		OpenStdin: true,
... ...
@@ -462,7 +471,7 @@ func TestCreateVolume(t *testing.T) {
462 462
 	if err != nil {
463 463
 		t.Fatal(err)
464 464
 	}
465
-	c, err := NewBuilder(runtime).Create(config)
465
+	c, err := runtime.Create(config)
466 466
 	if err != nil {
467 467
 		t.Fatal(err)
468 468
 	}
... ...
@@ -477,7 +486,7 @@ func TestCreateVolume(t *testing.T) {
477 477
 func TestKill(t *testing.T) {
478 478
 	runtime := mkRuntime(t)
479 479
 	defer nuke(runtime)
480
-	container, err := NewBuilder(runtime).Create(&Config{
480
+	container, err := runtime.Create(&Config{
481 481
 		Image: GetTestImage(runtime).ID,
482 482
 		Cmd:   []string{"sleep", "2"},
483 483
 	},
... ...
@@ -521,9 +530,7 @@ func TestExitCode(t *testing.T) {
521 521
 	runtime := mkRuntime(t)
522 522
 	defer nuke(runtime)
523 523
 
524
-	builder := NewBuilder(runtime)
525
-
526
-	trueContainer, err := builder.Create(&Config{
524
+	trueContainer, err := runtime.Create(&Config{
527 525
 		Image: GetTestImage(runtime).ID,
528 526
 		Cmd:   []string{"/bin/true", ""},
529 527
 	})
... ...
@@ -538,7 +545,7 @@ func TestExitCode(t *testing.T) {
538 538
 		t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode)
539 539
 	}
540 540
 
541
-	falseContainer, err := builder.Create(&Config{
541
+	falseContainer, err := runtime.Create(&Config{
542 542
 		Image: GetTestImage(runtime).ID,
543 543
 		Cmd:   []string{"/bin/false", ""},
544 544
 	})
... ...
@@ -557,7 +564,7 @@ func TestExitCode(t *testing.T) {
557 557
 func TestRestart(t *testing.T) {
558 558
 	runtime := mkRuntime(t)
559 559
 	defer nuke(runtime)
560
-	container, err := NewBuilder(runtime).Create(&Config{
560
+	container, err := runtime.Create(&Config{
561 561
 		Image: GetTestImage(runtime).ID,
562 562
 		Cmd:   []string{"echo", "-n", "foobar"},
563 563
 	},
... ...
@@ -587,7 +594,7 @@ func TestRestart(t *testing.T) {
587 587
 func TestRestartStdin(t *testing.T) {
588 588
 	runtime := mkRuntime(t)
589 589
 	defer nuke(runtime)
590
-	container, err := NewBuilder(runtime).Create(&Config{
590
+	container, err := runtime.Create(&Config{
591 591
 		Image: GetTestImage(runtime).ID,
592 592
 		Cmd:   []string{"cat"},
593 593
 
... ...
@@ -664,10 +671,8 @@ func TestUser(t *testing.T) {
664 664
 	runtime := mkRuntime(t)
665 665
 	defer nuke(runtime)
666 666
 
667
-	builder := NewBuilder(runtime)
668
-
669 667
 	// Default user must be root
670
-	container, err := builder.Create(&Config{
668
+	container, err := runtime.Create(&Config{
671 669
 		Image: GetTestImage(runtime).ID,
672 670
 		Cmd:   []string{"id"},
673 671
 	},
... ...
@@ -685,7 +690,7 @@ func TestUser(t *testing.T) {
685 685
 	}
686 686
 
687 687
 	// Set a username
688
-	container, err = builder.Create(&Config{
688
+	container, err = runtime.Create(&Config{
689 689
 		Image: GetTestImage(runtime).ID,
690 690
 		Cmd:   []string{"id"},
691 691
 
... ...
@@ -705,7 +710,7 @@ func TestUser(t *testing.T) {
705 705
 	}
706 706
 
707 707
 	// Set a UID
708
-	container, err = builder.Create(&Config{
708
+	container, err = runtime.Create(&Config{
709 709
 		Image: GetTestImage(runtime).ID,
710 710
 		Cmd:   []string{"id"},
711 711
 
... ...
@@ -725,7 +730,7 @@ func TestUser(t *testing.T) {
725 725
 	}
726 726
 
727 727
 	// Set a different user by uid
728
-	container, err = builder.Create(&Config{
728
+	container, err = runtime.Create(&Config{
729 729
 		Image: GetTestImage(runtime).ID,
730 730
 		Cmd:   []string{"id"},
731 731
 
... ...
@@ -747,7 +752,7 @@ func TestUser(t *testing.T) {
747 747
 	}
748 748
 
749 749
 	// Set a different user by username
750
-	container, err = builder.Create(&Config{
750
+	container, err = runtime.Create(&Config{
751 751
 		Image: GetTestImage(runtime).ID,
752 752
 		Cmd:   []string{"id"},
753 753
 
... ...
@@ -767,7 +772,7 @@ func TestUser(t *testing.T) {
767 767
 	}
768 768
 
769 769
 	// Test an wrong username
770
-	container, err = builder.Create(&Config{
770
+	container, err = runtime.Create(&Config{
771 771
 		Image: GetTestImage(runtime).ID,
772 772
 		Cmd:   []string{"id"},
773 773
 
... ...
@@ -788,9 +793,7 @@ func TestMultipleContainers(t *testing.T) {
788 788
 	runtime := mkRuntime(t)
789 789
 	defer nuke(runtime)
790 790
 
791
-	builder := NewBuilder(runtime)
792
-
793
-	container1, err := builder.Create(&Config{
791
+	container1, err := runtime.Create(&Config{
794 792
 		Image: GetTestImage(runtime).ID,
795 793
 		Cmd:   []string{"sleep", "2"},
796 794
 	},
... ...
@@ -800,7 +803,7 @@ func TestMultipleContainers(t *testing.T) {
800 800
 	}
801 801
 	defer runtime.Destroy(container1)
802 802
 
803
-	container2, err := builder.Create(&Config{
803
+	container2, err := runtime.Create(&Config{
804 804
 		Image: GetTestImage(runtime).ID,
805 805
 		Cmd:   []string{"sleep", "2"},
806 806
 	},
... ...
@@ -844,7 +847,7 @@ func TestMultipleContainers(t *testing.T) {
844 844
 func TestStdin(t *testing.T) {
845 845
 	runtime := mkRuntime(t)
846 846
 	defer nuke(runtime)
847
-	container, err := NewBuilder(runtime).Create(&Config{
847
+	container, err := runtime.Create(&Config{
848 848
 		Image: GetTestImage(runtime).ID,
849 849
 		Cmd:   []string{"cat"},
850 850
 
... ...
@@ -889,7 +892,7 @@ func TestStdin(t *testing.T) {
889 889
 func TestTty(t *testing.T) {
890 890
 	runtime := mkRuntime(t)
891 891
 	defer nuke(runtime)
892
-	container, err := NewBuilder(runtime).Create(&Config{
892
+	container, err := runtime.Create(&Config{
893 893
 		Image: GetTestImage(runtime).ID,
894 894
 		Cmd:   []string{"cat"},
895 895
 
... ...
@@ -934,7 +937,7 @@ func TestTty(t *testing.T) {
934 934
 func TestEnv(t *testing.T) {
935 935
 	runtime := mkRuntime(t)
936 936
 	defer nuke(runtime)
937
-	container, err := NewBuilder(runtime).Create(&Config{
937
+	container, err := runtime.Create(&Config{
938 938
 		Image: GetTestImage(runtime).ID,
939 939
 		Cmd:   []string{"env"},
940 940
 	},
... ...
@@ -983,7 +986,7 @@ func TestEnv(t *testing.T) {
983 983
 func TestEntrypoint(t *testing.T) {
984 984
 	runtime := mkRuntime(t)
985 985
 	defer nuke(runtime)
986
-	container, err := NewBuilder(runtime).Create(
986
+	container, err := runtime.Create(
987 987
 		&Config{
988 988
 			Image:      GetTestImage(runtime).ID,
989 989
 			Entrypoint: []string{"/bin/echo"},
... ...
@@ -1006,7 +1009,7 @@ func TestEntrypoint(t *testing.T) {
1006 1006
 func TestEntrypointNoCmd(t *testing.T) {
1007 1007
 	runtime := mkRuntime(t)
1008 1008
 	defer nuke(runtime)
1009
-	container, err := NewBuilder(runtime).Create(
1009
+	container, err := runtime.Create(
1010 1010
 		&Config{
1011 1011
 			Image:      GetTestImage(runtime).ID,
1012 1012
 			Entrypoint: []string{"/bin/echo", "foobar"},
... ...
@@ -1057,7 +1060,7 @@ func TestLXCConfig(t *testing.T) {
1057 1057
 	cpuMin := 100
1058 1058
 	cpuMax := 10000
1059 1059
 	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
1060
-	container, err := NewBuilder(runtime).Create(&Config{
1060
+	container, err := runtime.Create(&Config{
1061 1061
 		Image: GetTestImage(runtime).ID,
1062 1062
 		Cmd:   []string{"/bin/true"},
1063 1063
 
... ...
@@ -1081,7 +1084,7 @@ func TestLXCConfig(t *testing.T) {
1081 1081
 func TestCustomLxcConfig(t *testing.T) {
1082 1082
 	runtime := mkRuntime(t)
1083 1083
 	defer nuke(runtime)
1084
-	container, err := NewBuilder(runtime).Create(&Config{
1084
+	container, err := runtime.Create(&Config{
1085 1085
 		Image: GetTestImage(runtime).ID,
1086 1086
 		Cmd:   []string{"/bin/true"},
1087 1087
 
... ...
@@ -1112,7 +1115,7 @@ func BenchmarkRunSequencial(b *testing.B) {
1112 1112
 	runtime := mkRuntime(b)
1113 1113
 	defer nuke(runtime)
1114 1114
 	for i := 0; i < b.N; i++ {
1115
-		container, err := NewBuilder(runtime).Create(&Config{
1115
+		container, err := runtime.Create(&Config{
1116 1116
 			Image: GetTestImage(runtime).ID,
1117 1117
 			Cmd:   []string{"echo", "-n", "foo"},
1118 1118
 		},
... ...
@@ -1144,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) {
1144 1144
 		complete := make(chan error)
1145 1145
 		tasks = append(tasks, complete)
1146 1146
 		go func(i int, complete chan error) {
1147
-			container, err := NewBuilder(runtime).Create(&Config{
1147
+			container, err := runtime.Create(&Config{
1148 1148
 				Image: GetTestImage(runtime).ID,
1149 1149
 				Cmd:   []string{"echo", "-n", "foo"},
1150 1150
 			},
... ...
@@ -1193,6 +1196,60 @@ func tempDir(t *testing.T) string {
1193 1193
 	return tmpDir
1194 1194
 }
1195 1195
 
1196
+// Test for #1582
1197
+func TestCopyVolumeContent(t *testing.T) {
1198
+	r := mkRuntime(t)
1199
+	defer nuke(r)
1200
+
1201
+	// Put some content in a directory of a container and commit it
1202
+	container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t)
1203
+	defer r.Destroy(container1)
1204
+
1205
+	if container1.State.Running {
1206
+		t.Errorf("Container shouldn't be running")
1207
+	}
1208
+	if err := container1.Run(); err != nil {
1209
+		t.Fatal(err)
1210
+	}
1211
+	if container1.State.Running {
1212
+		t.Errorf("Container shouldn't be running")
1213
+	}
1214
+
1215
+	rwTar, err := container1.ExportRw()
1216
+	if err != nil {
1217
+		t.Error(err)
1218
+	}
1219
+	img, err := r.graph.Create(rwTar, container1, "unit test commited image", "", nil)
1220
+	if err != nil {
1221
+		t.Error(err)
1222
+	}
1223
+
1224
+	// Test that the content is copied from the image to the volume
1225
+	tmpDir1 := tempDir(t)
1226
+	defer os.RemoveAll(tmpDir1)
1227
+	stdout1, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello", tmpDir1), img.ID, "find", "/hello"}, t)
1228
+	if !(strings.Contains(stdout1, "/hello/local/world") && strings.Contains(stdout1, "/hello/local")) {
1229
+		t.Fatal("Container failed to transfer content to volume")
1230
+	}
1231
+
1232
+	// Test that the content is not copied when the volume is readonly
1233
+	tmpDir2 := tempDir(t)
1234
+	defer os.RemoveAll(tmpDir2)
1235
+	stdout2, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:ro", tmpDir2), img.ID, "find", "/hello"}, t)
1236
+	if strings.Contains(stdout2, "/hello/local/world") || strings.Contains(stdout2, "/hello/local") {
1237
+		t.Fatal("Container transfered content to readonly volume")
1238
+	}
1239
+
1240
+	// Test that the content is not copied when the volume is non-empty
1241
+	tmpDir3 := tempDir(t)
1242
+	defer os.RemoveAll(tmpDir3)
1243
+	writeFile(path.Join(tmpDir3, "touch-me"), "", t)
1244
+	stdout3, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:rw", tmpDir3), img.ID, "find", "/hello"}, t)
1245
+	if strings.Contains(stdout3, "/hello/local/world") || strings.Contains(stdout3, "/hello/local") || !strings.Contains(stdout3, "/hello/touch-me") {
1246
+		t.Fatal("Container transfered content to non-empty volume")
1247
+	}
1248
+}
1249
+
1196 1250
 func TestBindMounts(t *testing.T) {
1197 1251
 	r := mkRuntime(t)
1198 1252
 	defer nuke(r)
... ...
@@ -1220,7 +1277,7 @@ func TestBindMounts(t *testing.T) {
1220 1220
 func TestVolumesFromReadonlyMount(t *testing.T) {
1221 1221
 	runtime := mkRuntime(t)
1222 1222
 	defer nuke(runtime)
1223
-	container, err := NewBuilder(runtime).Create(
1223
+	container, err := runtime.Create(
1224 1224
 		&Config{
1225 1225
 			Image:   GetTestImage(runtime).ID,
1226 1226
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
... ...
@@ -1239,7 +1296,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
1239 1239
 		t.Fail()
1240 1240
 	}
1241 1241
 
1242
-	container2, err := NewBuilder(runtime).Create(
1242
+	container2, err := runtime.Create(
1243 1243
 		&Config{
1244 1244
 			Image:       GetTestImage(runtime).ID,
1245 1245
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
... ...
@@ -1275,7 +1332,7 @@ func TestRestartWithVolumes(t *testing.T) {
1275 1275
 	runtime := mkRuntime(t)
1276 1276
 	defer nuke(runtime)
1277 1277
 
1278
-	container, err := NewBuilder(runtime).Create(&Config{
1278
+	container, err := runtime.Create(&Config{
1279 1279
 		Image:   GetTestImage(runtime).ID,
1280 1280
 		Cmd:     []string{"echo", "-n", "foobar"},
1281 1281
 		Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1318,7 +1375,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
1318 1318
 	runtime := mkRuntime(t)
1319 1319
 	defer nuke(runtime)
1320 1320
 
1321
-	container, err := NewBuilder(runtime).Create(&Config{
1321
+	container, err := runtime.Create(&Config{
1322 1322
 		Image:   GetTestImage(runtime).ID,
1323 1323
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
1324 1324
 		Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1345,7 +1402,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
1345 1345
 		t.Fail()
1346 1346
 	}
1347 1347
 
1348
-	container2, err := NewBuilder(runtime).Create(
1348
+	container2, err := runtime.Create(
1349 1349
 		&Config{
1350 1350
 			Image:       GetTestImage(runtime).ID,
1351 1351
 			Cmd:         []string{"cat", "/test/foo"},
... ...
@@ -1386,7 +1443,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
1386 1386
 	if err != nil {
1387 1387
 		t.Fatal(err)
1388 1388
 	}
1389
-	c, err := NewBuilder(runtime).Create(config)
1389
+	c, err := runtime.Create(config)
1390 1390
 	if err != nil {
1391 1391
 		t.Fatal(err)
1392 1392
 	}
... ...
@@ -1 +1 @@
1
-# Maintainer wanted! Enroll on #docker@freenode
1
+Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
... ...
@@ -21,7 +21,6 @@
21 21
 # If the docker daemon is using a unix socket for communication your user
22 22
 # must have access to the socket for the completions to function correctly
23 23
 
24
-have docker && {
25 24
 __docker_containers_all()
26 25
 {
27 26
 	local containers
... ...
@@ -542,4 +541,3 @@ _docker()
542 542
 }
543 543
 
544 544
 complete -F _docker docker
545
-}
546 545
\ No newline at end of file
... ...
@@ -47,7 +47,7 @@ else
47 47
   echo "Creating /etc/init/dockerd.conf..."
48 48
   cat >/etc/init/dockerd.conf <<EOF
49 49
 description "Docker daemon"
50
-start on filesystem or runlevel [2345]
50
+start on filesystem and started lxc-net
51 51
 stop on runlevel [!2345]
52 52
 respawn
53 53
 exec /usr/local/bin/docker -d
... ...
@@ -16,7 +16,7 @@ import (
16 16
 
17 17
 var (
18 18
 	GITCOMMIT string
19
-	VERSION string
19
+	VERSION   string
20 20
 )
21 21
 
22 22
 func main() {
... ...
@@ -75,6 +75,9 @@ func main() {
75 75
 		}
76 76
 		protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
77 77
 		if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
78
+			if sterr, ok := err.(*utils.StatusError); ok {
79
+				os.Exit(sterr.Status)
80
+			}
78 81
 			log.Fatal(err)
79 82
 			os.Exit(-1)
80 83
 		}
81 84
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+from ubuntu:12.04
1
+maintainer Nick Stinemates
2
+
3
+run apt-get update
4
+run apt-get install -y python-setuptools make
5
+run easy_install pip
6
+add . /docs
7
+run pip install -r /docs/requirements.txt
8
+run cd /docs; make docs
9
+
10
+expose 8000
11
+
12
+workdir /docs/_build/html
13
+
14
+entrypoint ["python", "-m", "SimpleHTTPServer"]
... ...
@@ -27,14 +27,36 @@ Docker Remote API
27 27
 2. Versions
28 28
 ===========
29 29
 
30
-The current version of the API is 1.4
30
+The current version of the API is 1.5
31 31
 
32 32
 Calling /images/<name>/insert is the same as calling
33
-/v1.4/images/<name>/insert 
33
+/v1.5/images/<name>/insert 
34 34
 
35 35
 You can still call an old version of the api using
36 36
 /v1.0/images/<name>/insert
37 37
 
38
+:doc:`docker_remote_api_v1.5`
39
+*****************************
40
+
41
+What's new
42
+----------
43
+
44
+.. http:post:: /images/create
45
+
46
+   **New!** You can now pass registry credentials (via an AuthConfig object)
47
+   through the `X-Registry-Auth` header
48
+
49
+.. http:post:: /images/(name)/push
50
+
51
+   **New!** The AuthConfig object now needs to be passed through 
52
+   the `X-Registry-Auth` header
53
+
54
+.. http:get:: /containers/json
55
+
56
+   **New!** The format of the `Ports` entry has been changed to a list of
57
+   dicts each containing `PublicPort`, `PrivatePort` and `Type` describing a
58
+   port mapping.
59
+
38 60
 :doc:`docker_remote_api_v1.4`
39 61
 *****************************
40 62
 
... ...
@@ -175,12 +197,13 @@ and we will add the libraries here.
175 175
 +======================+================+============================================+
176 176
 | Python               | docker-py      | https://github.com/dotcloud/docker-py      |
177 177
 +----------------------+----------------+--------------------------------------------+
178
-| Ruby                 | docker-ruby    | https://github.com/ActiveState/docker-ruby |
179
-+----------------------+----------------+--------------------------------------------+
180 178
 | Ruby                 | docker-client  | https://github.com/geku/docker-client      |
181 179
 +----------------------+----------------+--------------------------------------------+
182 180
 | Ruby                 | docker-api     | https://github.com/swipely/docker-api      |
183 181
 +----------------------+----------------+--------------------------------------------+
182
+| Javascript (NodeJS)  | docker.io      | https://github.com/appersonlabs/docker.io  |
183
+|                      |                | Install via NPM: `npm install docker.io`   |
184
++----------------------+----------------+--------------------------------------------+
184 185
 | Javascript           | docker-js      | https://github.com/dgoujard/docker-js      |
185 186
 +----------------------+----------------+--------------------------------------------+
186 187
 | Javascript (Angular) | dockerui       | https://github.com/crosbymichael/dockerui  |
... ...
@@ -188,4 +211,7 @@ and we will add the libraries here.
188 188
 +----------------------+----------------+--------------------------------------------+
189 189
 | Java                 | docker-java    | https://github.com/kpelykh/docker-java     |
190 190
 +----------------------+----------------+--------------------------------------------+
191
-
191
+| Erlang               | erldocker      | https://github.com/proger/erldocker        |
192
++----------------------+----------------+--------------------------------------------+
193
+| Go                   | go-dockerclient| https://github.com/fsouza/go-dockerclient  |
194
++----------------------+----------------+--------------------------------------------+
... ...
@@ -119,6 +119,7 @@ Create a container
119 119
 		"AttachStdout":true,
120 120
 		"AttachStderr":true,
121 121
 		"PortSpecs":null,
122
+		"Privileged": false,
122 123
 		"Tty":false,
123 124
 		"OpenStdin":false,
124 125
 		"StdinOnce":false,
... ...
@@ -223,6 +224,7 @@ Inspect a container
223 223
 
224 224
 	:statuscode 200: no error
225 225
 	:statuscode 404: no such container
226
+	:statuscode 409: conflict between containers and images
226 227
 	:statuscode 500: server error
227 228
 
228 229
 
... ...
@@ -679,8 +681,8 @@ Create an image
679 679
         :statuscode 500: server error
680 680
 
681 681
 
682
-Insert a file in a image
683
-************************
682
+Insert a file in an image
683
+*************************
684 684
 
685 685
 .. http:post:: /images/(name)/insert
686 686
 
... ...
@@ -759,7 +761,8 @@ Inspect an image
759 759
 
760 760
 	:statuscode 200: no error
761 761
 	:statuscode 404: no such image
762
-        :statuscode 500: server error
762
+	:statuscode 409: conflict between containers and images
763
+	:statuscode 500: server error
763 764
 
764 765
 
765 766
 Get the history of an image
... ...
@@ -990,7 +993,8 @@ Check auth configuration
990 990
 	   {
991 991
 		"username":"hannibal",
992 992
 		"password:"xxxx",
993
-		"email":"hannibal@a-team.com"
993
+		"email":"hannibal@a-team.com",
994
+		"serveraddress":"https://index.docker.io/v1/"
994 995
 	   }
995 996
 
996 997
         **Example response**:
... ...
@@ -1082,7 +1086,7 @@ Create a new image from a container's changes
1082 1082
 
1083 1083
         POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
1084 1084
 
1085
-        **Example response**:
1085
+    **Example response**:
1086 1086
 
1087 1087
     .. sourcecode:: http
1088 1088
 
... ...
@@ -1091,15 +1095,15 @@ Create a new image from a container's changes
1091 1091
 
1092 1092
         {"Id":"596069db4bf5"}
1093 1093
 
1094
-	:query container: source container
1095
-	:query repo: repository
1096
-	:query tag: tag
1097
-	:query m: commit message
1098
-	:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
1099
-	:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
1100
-        :statuscode 201: no error
1101
-	:statuscode 404: no such container
1102
-        :statuscode 500: server error
1094
+    :query container: source container
1095
+    :query repo: repository
1096
+    :query tag: tag
1097
+    :query m: commit message
1098
+    :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
1099
+    :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
1100
+    :statuscode 201: no error
1101
+    :statuscode 404: no such container
1102
+    :statuscode 500: server error
1103 1103
 
1104 1104
 
1105 1105
 Monitor Docker's events
1106 1106
new file mode 100644
... ...
@@ -0,0 +1,1175 @@
0
+:title: Remote API v1.5
1
+:description: API Documentation for Docker
2
+:keywords: API, Docker, rcli, REST, documentation
3
+
4
+:orphan:
5
+
6
+======================
7
+Docker Remote API v1.5
8
+======================
9
+
10
+.. contents:: Table of Contents
11
+
12
+1. Brief introduction
13
+=====================
14
+
15
+- The Remote API is replacing rcli
16
+- Default port in the docker daemon is 4243
17
+- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
18
+
19
+2. Endpoints
20
+============
21
+
22
+2.1 Containers
23
+--------------
24
+
25
+List containers
26
+***************
27
+
28
+.. http:get:: /containers/json
29
+
30
+	List containers
31
+
32
+	**Example request**:
33
+
34
+	.. sourcecode:: http
35
+
36
+	   GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1
37
+	   
38
+	**Example response**:
39
+
40
+	.. sourcecode:: http
41
+
42
+	   HTTP/1.1 200 OK
43
+	   Content-Type: application/json
44
+	   
45
+	   [
46
+		{
47
+			"Id": "8dfafdbc3a40",
48
+			"Image": "base:latest",
49
+			"Command": "echo 1",
50
+			"Created": 1367854155,
51
+			"Status": "Exit 0",
52
+			"Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
53
+			"SizeRw":12288,
54
+			"SizeRootFs":0
55
+		},
56
+		{
57
+			"Id": "9cd87474be90",
58
+			"Image": "base:latest",
59
+			"Command": "echo 222222",
60
+			"Created": 1367854155,
61
+			"Status": "Exit 0",
62
+			"Ports":[],
63
+			"SizeRw":12288,
64
+			"SizeRootFs":0
65
+		},
66
+		{
67
+			"Id": "3176a2479c92",
68
+			"Image": "base:latest",
69
+			"Command": "echo 3333333333333333",
70
+			"Created": 1367854154,
71
+			"Status": "Exit 0",
72
+			"Ports":[],
73
+			"SizeRw":12288,
74
+			"SizeRootFs":0
75
+		},
76
+		{
77
+			"Id": "4cb07b47f9fb",
78
+			"Image": "base:latest",
79
+			"Command": "echo 444444444444444444444444444444444",
80
+			"Created": 1367854152,
81
+			"Status": "Exit 0",
82
+			"Ports":[],
83
+			"SizeRw":12288,
84
+			"SizeRootFs":0
85
+		}
86
+	   ]
87
+ 
88
+	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
89
+	:query limit: Show ``limit`` last created containers, include non-running ones.
90
+	:query since: Show only containers created since Id, include non-running ones.
91
+	:query before: Show only containers created before Id, include non-running ones.
92
+	:query size: 1/True/true or 0/False/false, Show the containers sizes
93
+	:statuscode 200: no error
94
+	:statuscode 400: bad parameter
95
+	:statuscode 500: server error
96
+
97
+
98
+Create a container
99
+******************
100
+
101
+.. http:post:: /containers/create
102
+
103
+	Create a container
104
+
105
+	**Example request**:
106
+
107
+	.. sourcecode:: http
108
+
109
+	   POST /containers/create HTTP/1.1
110
+	   Content-Type: application/json
111
+
112
+	   {
113
+		"Hostname":"",
114
+		"User":"",
115
+		"Memory":0,
116
+		"MemorySwap":0,
117
+		"AttachStdin":false,
118
+		"AttachStdout":true,
119
+		"AttachStderr":true,
120
+		"PortSpecs":null,
121
+		"Privileged": false,
122
+		"Tty":false,
123
+		"OpenStdin":false,
124
+		"StdinOnce":false,
125
+		"Env":null,
126
+		"Cmd":[
127
+			"date"
128
+		],
129
+		"Dns":null,
130
+		"Image":"base",
131
+		"Volumes":{},
132
+		"VolumesFrom":"",
133
+		"WorkingDir":""
134
+
135
+	   }
136
+	   
137
+	**Example response**:
138
+
139
+	.. sourcecode:: http
140
+
141
+	   HTTP/1.1 201 OK
142
+	   Content-Type: application/json
143
+
144
+	   {
145
+		"Id":"e90e34656806"
146
+		"Warnings":[]
147
+	   }
148
+	
149
+	:jsonparam config: the container's configuration
150
+	:statuscode 201: no error
151
+	:statuscode 404: no such container
152
+	:statuscode 406: impossible to attach (container not running)
153
+	:statuscode 500: server error
154
+
155
+
156
+Inspect a container
157
+*******************
158
+
159
+.. http:get:: /containers/(id)/json
160
+
161
+	Return low-level information on the container ``id``
162
+
163
+	**Example request**:
164
+
165
+	.. sourcecode:: http
166
+
167
+	   GET /containers/4fa6e0f0c678/json HTTP/1.1
168
+	   
169
+	**Example response**:
170
+
171
+	.. sourcecode:: http
172
+
173
+	   HTTP/1.1 200 OK
174
+	   Content-Type: application/json
175
+
176
+	   {
177
+			"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
178
+			"Created": "2013-05-07T14:51:42.041847+02:00",
179
+			"Path": "date",
180
+			"Args": [],
181
+			"Config": {
182
+				"Hostname": "4fa6e0f0c678",
183
+				"User": "",
184
+				"Memory": 0,
185
+				"MemorySwap": 0,
186
+				"AttachStdin": false,
187
+				"AttachStdout": true,
188
+				"AttachStderr": true,
189
+				"PortSpecs": null,
190
+				"Tty": false,
191
+				"OpenStdin": false,
192
+				"StdinOnce": false,
193
+				"Env": null,
194
+				"Cmd": [
195
+					"date"
196
+				],
197
+				"Dns": null,
198
+				"Image": "base",
199
+				"Volumes": {},
200
+				"VolumesFrom": "",
201
+				"WorkingDir":""
202
+
203
+			},
204
+			"State": {
205
+				"Running": false,
206
+				"Pid": 0,
207
+				"ExitCode": 0,
208
+				"StartedAt": "2013-05-07T14:51:42.087658+02:01360",
209
+				"Ghost": false
210
+			},
211
+			"Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
212
+			"NetworkSettings": {
213
+				"IpAddress": "",
214
+				"IpPrefixLen": 0,
215
+				"Gateway": "",
216
+				"Bridge": "",
217
+				"PortMapping": null
218
+			},
219
+			"SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
220
+			"ResolvConfPath": "/etc/resolv.conf",
221
+			"Volumes": {}
222
+	   }
223
+
224
+	:statuscode 200: no error
225
+	:statuscode 404: no such container
226
+	:statuscode 500: server error
227
+
228
+
229
+List processes running inside a container
230
+*****************************************
231
+
232
+.. http:get:: /containers/(id)/top
233
+
234
+	List processes running inside the container ``id``
235
+
236
+	**Example request**:
237
+
238
+	.. sourcecode:: http
239
+
240
+	   GET /containers/4fa6e0f0c678/top HTTP/1.1
241
+
242
+	**Example response**:
243
+
244
+	.. sourcecode:: http
245
+
246
+	   HTTP/1.1 200 OK
247
+	   Content-Type: application/json
248
+
249
+	   {
250
+		"Titles":[
251
+			"USER",
252
+			"PID",
253
+			"%CPU",
254
+			"%MEM",
255
+			"VSZ",
256
+			"RSS",
257
+			"TTY",
258
+			"STAT",
259
+			"START",
260
+			"TIME",
261
+			"COMMAND"
262
+			],
263
+		"Processes":[
264
+			["root","20147","0.0","0.1","18060","1864","pts/4","S","10:06","0:00","bash"],
265
+			["root","20271","0.0","0.0","4312","352","pts/4","S+","10:07","0:00","sleep","10"]
266
+		]
267
+	   }
268
+
269
+	:query ps_args: ps arguments to use (eg. aux)
270
+	:statuscode 200: no error
271
+	:statuscode 404: no such container
272
+	:statuscode 500: server error
273
+
274
+
275
+Inspect changes on a container's filesystem
276
+*******************************************
277
+
278
+.. http:get:: /containers/(id)/changes
279
+
280
+	Inspect changes on container ``id`` 's filesystem
281
+
282
+	**Example request**:
283
+
284
+	.. sourcecode:: http
285
+
286
+	   GET /containers/4fa6e0f0c678/changes HTTP/1.1
287
+
288
+	   
289
+	**Example response**:
290
+
291
+	.. sourcecode:: http
292
+
293
+	   HTTP/1.1 200 OK
294
+	   Content-Type: application/json
295
+	   
296
+	   [
297
+		{
298
+			"Path":"/dev",
299
+			"Kind":0
300
+		},
301
+		{
302
+			"Path":"/dev/kmsg",
303
+			"Kind":1
304
+		},
305
+		{
306
+			"Path":"/test",
307
+			"Kind":1
308
+		}
309
+	   ]
310
+
311
+	:statuscode 200: no error
312
+	:statuscode 404: no such container
313
+	:statuscode 500: server error
314
+
315
+
316
+Export a container
317
+******************
318
+
319
+.. http:get:: /containers/(id)/export
320
+
321
+	Export the contents of container ``id``
322
+
323
+	**Example request**:
324
+
325
+	.. sourcecode:: http
326
+
327
+	   GET /containers/4fa6e0f0c678/export HTTP/1.1
328
+
329
+	   
330
+	**Example response**:
331
+
332
+	.. sourcecode:: http
333
+
334
+	   HTTP/1.1 200 OK
335
+	   Content-Type: application/octet-stream
336
+	   
337
+	   {{ STREAM }}
338
+
339
+	:statuscode 200: no error
340
+	:statuscode 404: no such container
341
+	:statuscode 500: server error
342
+
343
+
344
+Start a container
345
+*****************
346
+
347
+.. http:post:: /containers/(id)/start
348
+
349
+        Start the container ``id``
350
+
351
+        **Example request**:
352
+
353
+        .. sourcecode:: http
354
+
355
+           POST /containers/(id)/start HTTP/1.1
356
+           Content-Type: application/json
357
+
358
+           {
359
+                "Binds":["/tmp:/tmp"],
360
+                "LxcConf":{"lxc.utsname":"docker"}
361
+           }
362
+
363
+        **Example response**:
364
+
365
+        .. sourcecode:: http
366
+
367
+           HTTP/1.1 204 No Content
368
+           Content-Type: text/plain
369
+
370
+        :jsonparam hostConfig: the container's host configuration (optional)
371
+        :statuscode 204: no error
372
+        :statuscode 404: no such container
373
+        :statuscode 500: server error
374
+
375
+
376
+Stop a container
377
+****************
378
+
379
+.. http:post:: /containers/(id)/stop
380
+
381
+	Stop the container ``id``
382
+
383
+	**Example request**:
384
+
385
+	.. sourcecode:: http
386
+
387
+	   POST /containers/e90e34656806/stop?t=5 HTTP/1.1
388
+	   
389
+	**Example response**:
390
+
391
+	.. sourcecode:: http
392
+
393
+	   HTTP/1.1 204 OK
394
+	   	
395
+	:query t: number of seconds to wait before killing the container
396
+	:statuscode 204: no error
397
+	:statuscode 404: no such container
398
+	:statuscode 500: server error
399
+
400
+
401
+Restart a container
402
+*******************
403
+
404
+.. http:post:: /containers/(id)/restart
405
+
406
+	Restart the container ``id``
407
+
408
+	**Example request**:
409
+
410
+	.. sourcecode:: http
411
+
412
+	   POST /containers/e90e34656806/restart?t=5 HTTP/1.1
413
+	   
414
+	**Example response**:
415
+
416
+	.. sourcecode:: http
417
+
418
+	   HTTP/1.1 204 OK
419
+	   	
420
+	:query t: number of seconds to wait before killing the container
421
+	:statuscode 204: no error
422
+	:statuscode 404: no such container
423
+	:statuscode 500: server error
424
+
425
+
426
+Kill a container
427
+****************
428
+
429
+.. http:post:: /containers/(id)/kill
430
+
431
+	Kill the container ``id``
432
+
433
+	**Example request**:
434
+
435
+	.. sourcecode:: http
436
+
437
+	   POST /containers/e90e34656806/kill HTTP/1.1
438
+	   
439
+	**Example response**:
440
+
441
+	.. sourcecode:: http
442
+
443
+	   HTTP/1.1 204 OK
444
+	   	
445
+	:statuscode 204: no error
446
+	:statuscode 404: no such container
447
+	:statuscode 500: server error
448
+
449
+
450
+Attach to a container
451
+*********************
452
+
453
+.. http:post:: /containers/(id)/attach
454
+
455
+	Attach to the container ``id``
456
+
457
+	**Example request**:
458
+
459
+	.. sourcecode:: http
460
+
461
+	   POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1
462
+	   
463
+	**Example response**:
464
+
465
+	.. sourcecode:: http
466
+
467
+	   HTTP/1.1 200 OK
468
+	   Content-Type: application/vnd.docker.raw-stream
469
+
470
+	   {{ STREAM }}
471
+	   	
472
+	:query logs: 1/True/true or 0/False/false, return logs. Default false
473
+	:query stream: 1/True/true or 0/False/false, return stream. Default false
474
+	:query stdin: 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false
475
+	:query stdout: 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false
476
+	:query stderr: 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false
477
+	:statuscode 200: no error
478
+	:statuscode 400: bad parameter
479
+	:statuscode 404: no such container
480
+	:statuscode 500: server error
481
+
482
+
483
+Wait a container
484
+****************
485
+
486
+.. http:post:: /containers/(id)/wait
487
+
488
+	Block until container ``id`` stops, then returns the exit code
489
+
490
+	**Example request**:
491
+
492
+	.. sourcecode:: http
493
+
494
+	   POST /containers/16253994b7c4/wait HTTP/1.1
495
+	   
496
+	**Example response**:
497
+
498
+	.. sourcecode:: http
499
+
500
+	   HTTP/1.1 200 OK
501
+	   Content-Type: application/json
502
+
503
+	   {"StatusCode":0}
504
+	   	
505
+	:statuscode 200: no error
506
+	:statuscode 404: no such container
507
+	:statuscode 500: server error
508
+
509
+
510
+Remove a container
511
+*******************
512
+
513
+.. http:delete:: /containers/(id)
514
+
515
+	Remove the container ``id`` from the filesystem
516
+
517
+	**Example request**:
518
+
519
+        .. sourcecode:: http
520
+
521
+           DELETE /containers/16253994b7c4?v=1 HTTP/1.1
522
+
523
+        **Example response**:
524
+
525
+        .. sourcecode:: http
526
+
527
+	   HTTP/1.1 204 OK
528
+
529
+	:query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false
530
+        :statuscode 204: no error
531
+	:statuscode 400: bad parameter
532
+        :statuscode 404: no such container
533
+        :statuscode 500: server error
534
+
535
+
536
+Copy files or folders from a container
537
+**************************************
538
+
539
+.. http:post:: /containers/(id)/copy
540
+
541
+	Copy files or folders of container ``id``
542
+
543
+	**Example request**:
544
+
545
+	.. sourcecode:: http
546
+
547
+	   POST /containers/4fa6e0f0c678/copy HTTP/1.1
548
+	   Content-Type: application/json
549
+
550
+	   {
551
+		"Resource":"test.txt"
552
+	   }
553
+
554
+	**Example response**:
555
+
556
+	.. sourcecode:: http
557
+
558
+	   HTTP/1.1 200 OK
559
+	   Content-Type: application/octet-stream
560
+	   
561
+	   {{ STREAM }}
562
+
563
+	:statuscode 200: no error
564
+	:statuscode 404: no such container
565
+	:statuscode 500: server error
566
+
567
+
568
+2.2 Images
569
+----------
570
+
571
+List Images
572
+***********
573
+
574
+.. http:get:: /images/(format)
575
+
576
+	List images ``format`` could be json or viz (json default)
577
+
578
+	**Example request**:
579
+
580
+	.. sourcecode:: http
581
+
582
+	   GET /images/json?all=0 HTTP/1.1
583
+
584
+	**Example response**:
585
+
586
+	.. sourcecode:: http
587
+
588
+	   HTTP/1.1 200 OK
589
+	   Content-Type: application/json
590
+	   
591
+	   [
592
+		{
593
+			"Repository":"base",
594
+			"Tag":"ubuntu-12.10",
595
+			"Id":"b750fe79269d",
596
+			"Created":1364102658,
597
+			"Size":24653,
598
+			"VirtualSize":180116135
599
+		},
600
+		{
601
+			"Repository":"base",
602
+			"Tag":"ubuntu-quantal",
603
+			"Id":"b750fe79269d",
604
+			"Created":1364102658,
605
+			"Size":24653,
606
+			"VirtualSize":180116135
607
+		}
608
+	   ]
609
+
610
+
611
+	**Example request**:
612
+
613
+	.. sourcecode:: http
614
+
615
+	   GET /images/viz HTTP/1.1
616
+
617
+	**Example response**:
618
+
619
+	.. sourcecode:: http
620
+
621
+	   HTTP/1.1 200 OK
622
+	   Content-Type: text/plain
623
+
624
+	   digraph docker {
625
+	   "d82cbacda43a" -> "074be284591f"
626
+	   "1496068ca813" -> "08306dc45919"
627
+	   "08306dc45919" -> "0e7893146ac2"
628
+	   "b750fe79269d" -> "1496068ca813"
629
+	   base -> "27cf78414709" [style=invis]
630
+	   "f71189fff3de" -> "9a33b36209ed"
631
+	   "27cf78414709" -> "b750fe79269d"
632
+	   "0e7893146ac2" -> "d6434d954665"
633
+	   "d6434d954665" -> "d82cbacda43a"
634
+	   base -> "e9aa60c60128" [style=invis]
635
+	   "074be284591f" -> "f71189fff3de"
636
+	   "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
637
+	   "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
638
+	   "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
639
+	   base [style=invisible]
640
+	   }
641
+ 
642
+	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
643
+	:statuscode 200: no error
644
+	:statuscode 400: bad parameter
645
+	:statuscode 500: server error
646
+
647
+
648
+Create an image
649
+***************
650
+
651
+.. http:post:: /images/create
652
+
653
+	Create an image, either by pull it from the registry or by importing it
654
+
655
+	**Example request**:
656
+
657
+        .. sourcecode:: http
658
+
659
+           POST /images/create?fromImage=base HTTP/1.1
660
+
661
+        **Example response**:
662
+
663
+        .. sourcecode:: http
664
+
665
+           HTTP/1.1 200 OK
666
+	   Content-Type: application/json
667
+
668
+	   {"status":"Pulling..."}
669
+	   {"status":"Pulling", "progress":"1/? (n/a)"}
670
+	   {"error":"Invalid..."}
671
+	   ...
672
+
673
+	When using this endpoint to pull an image from the registry,
674
+	the ``X-Registry-Auth`` header can be used to include a
675
+	base64-encoded AuthConfig object.
676
+
677
+        :query fromImage: name of the image to pull
678
+	:query fromSrc: source to import, - means stdin
679
+        :query repo: repository
680
+	:query tag: tag
681
+	:query registry: the registry to pull from
682
+        :statuscode 200: no error
683
+        :statuscode 500: server error
684
+
685
+
686
+Insert a file in an image
687
+*************************
688
+
689
+.. http:post:: /images/(name)/insert
690
+
691
+	Insert a file from ``url`` in the image ``name`` at ``path``
692
+
693
+	**Example request**:
694
+
695
+        .. sourcecode:: http
696
+
697
+           POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
698
+
699
+	**Example response**:
700
+
701
+        .. sourcecode:: http
702
+
703
+           HTTP/1.1 200 OK
704
+	   Content-Type: application/json
705
+
706
+	   {"status":"Inserting..."}
707
+	   {"status":"Inserting", "progress":"1/? (n/a)"}
708
+	   {"error":"Invalid..."}
709
+	   ...
710
+
711
+	:statuscode 200: no error
712
+        :statuscode 500: server error
713
+
714
+
715
+Inspect an image
716
+****************
717
+
718
+.. http:get:: /images/(name)/json
719
+
720
+	Return low-level information on the image ``name``
721
+
722
+	**Example request**:
723
+
724
+	.. sourcecode:: http
725
+
726
+	   GET /images/base/json HTTP/1.1
727
+
728
+	**Example response**:
729
+
730
+        .. sourcecode:: http
731
+
732
+           HTTP/1.1 200 OK
733
+	   Content-Type: application/json
734
+
735
+	   {
736
+		"id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
737
+		"parent":"27cf784147099545",
738
+		"created":"2013-03-23T22:24:18.818426-07:00",
739
+		"container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
740
+		"container_config":
741
+			{
742
+				"Hostname":"",
743
+				"User":"",
744
+				"Memory":0,
745
+				"MemorySwap":0,
746
+				"AttachStdin":false,
747
+				"AttachStdout":false,
748
+				"AttachStderr":false,
749
+				"PortSpecs":null,
750
+				"Tty":true,
751
+				"OpenStdin":true,
752
+				"StdinOnce":false,
753
+				"Env":null,
754
+				"Cmd": ["/bin/bash"]
755
+				,"Dns":null,
756
+				"Image":"base",
757
+				"Volumes":null,
758
+				"VolumesFrom":"",
759
+				"WorkingDir":""
760
+			},
761
+		"Size": 6824592
762
+	   }
763
+
764
+	:statuscode 200: no error
765
+	:statuscode 404: no such image
766
+        :statuscode 500: server error
767
+
768
+
769
+Get the history of an image
770
+***************************
771
+
772
+.. http:get:: /images/(name)/history
773
+
774
+        Return the history of the image ``name``
775
+
776
+        **Example request**:
777
+
778
+        .. sourcecode:: http
779
+
780
+           GET /images/base/history HTTP/1.1
781
+
782
+        **Example response**:
783
+
784
+        .. sourcecode:: http
785
+
786
+           HTTP/1.1 200 OK
787
+	   Content-Type: application/json
788
+
789
+	   [
790
+		{
791
+			"Id":"b750fe79269d",
792
+			"Created":1364102658,
793
+			"CreatedBy":"/bin/bash"
794
+		},
795
+		{
796
+			"Id":"27cf78414709",
797
+			"Created":1364068391,
798
+			"CreatedBy":""
799
+		}
800
+	   ]
801
+
802
+        :statuscode 200: no error
803
+        :statuscode 404: no such image
804
+        :statuscode 500: server error
805
+
806
+
807
+Push an image on the registry
808
+*****************************
809
+
810
+.. http:post:: /images/(name)/push
811
+
812
+   Push the image ``name`` on the registry
813
+
814
+   **Example request**:
815
+
816
+   .. sourcecode:: http
817
+
818
+      POST /images/test/push HTTP/1.1
819
+
820
+   **Example response**:
821
+
822
+   .. sourcecode:: http
823
+
824
+    HTTP/1.1 200 OK
825
+    Content-Type: application/json
826
+
827
+   {"status":"Pushing..."}
828
+   {"status":"Pushing", "progress":"1/? (n/a)"}
829
+   {"error":"Invalid..."}
830
+   ...
831
+
832
+	The ``X-Registry-Auth`` header can be used to include a
833
+	base64-encoded AuthConfig object.
834
+
835
+   :query registry: the registry you wan to push, optional
836
+   :statuscode 200: no error
837
+        :statuscode 404: no such image
838
+        :statuscode 500: server error
839
+
840
+
841
+Tag an image into a repository
842
+******************************
843
+
844
+.. http:post:: /images/(name)/tag
845
+
846
+	Tag the image ``name`` into a repository
847
+
848
+        **Example request**:
849
+
850
+        .. sourcecode:: http
851
+			
852
+	   POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1
853
+
854
+	**Example response**:
855
+
856
+        .. sourcecode:: http
857
+
858
+           HTTP/1.1 200 OK
859
+
860
+	:query repo: The repository to tag in
861
+	:query force: 1/True/true or 0/False/false, default false
862
+	:statuscode 200: no error
863
+	:statuscode 400: bad parameter
864
+	:statuscode 404: no such image
865
+	:statuscode 409: conflict
866
+        :statuscode 500: server error
867
+
868
+
869
+Remove an image
870
+***************
871
+
872
+.. http:delete:: /images/(name)
873
+
874
+	Remove the image ``name`` from the filesystem 
875
+	
876
+	**Example request**:
877
+
878
+	.. sourcecode:: http
879
+
880
+	   DELETE /images/test HTTP/1.1
881
+
882
+	**Example response**:
883
+
884
+        .. sourcecode:: http
885
+
886
+	   HTTP/1.1 200 OK
887
+	   Content-type: application/json
888
+
889
+	   [
890
+	    {"Untagged":"3e2f21a89f"},
891
+	    {"Deleted":"3e2f21a89f"},
892
+	    {"Deleted":"53b4f83ac9"}
893
+	   ]
894
+
895
+	:statuscode 200: no error
896
+        :statuscode 404: no such image
897
+	:statuscode 409: conflict
898
+        :statuscode 500: server error
899
+
900
+
901
+Search images
902
+*************
903
+
904
+.. http:get:: /images/search
905
+
906
+	Search for an image in the docker index
907
+	
908
+	**Example request**:
909
+
910
+        .. sourcecode:: http
911
+
912
+           GET /images/search?term=sshd HTTP/1.1
913
+
914
+	**Example response**:
915
+
916
+	.. sourcecode:: http
917
+
918
+	   HTTP/1.1 200 OK
919
+	   Content-Type: application/json
920
+	   
921
+	   [
922
+		{
923
+			"Name":"cespare/sshd",
924
+			"Description":""
925
+		},
926
+		{
927
+			"Name":"johnfuller/sshd",
928
+			"Description":""
929
+		},
930
+		{
931
+			"Name":"dhrp/mongodb-sshd",
932
+			"Description":""
933
+		}
934
+	   ]
935
+
936
+	   :query term: term to search
937
+	   :statuscode 200: no error
938
+	   :statuscode 500: server error
939
+
940
+
941
+2.3 Misc
942
+--------
943
+
944
+Build an image from Dockerfile via stdin
945
+****************************************
946
+
947
+.. http:post:: /build
948
+
949
+   Build an image from Dockerfile via stdin
950
+
951
+   **Example request**:
952
+
953
+   .. sourcecode:: http
954
+
955
+      POST /build HTTP/1.1
956
+
957
+      {{ STREAM }}
958
+
959
+   **Example response**:
960
+
961
+   .. sourcecode:: http
962
+
963
+      HTTP/1.1 200 OK
964
+
965
+      {{ STREAM }}
966
+
967
+
968
+       The stream must be a tar archive compressed with one of the following algorithms:
969
+       identity (no compression), gzip, bzip2, xz. The archive must include a file called
970
+       `Dockerfile` at its root. It may include any number of other files, which will be
971
+       accessible in the build context (See the ADD build command).
972
+
973
+       The Content-type header should be set to "application/tar".
974
+
975
+	:query t: repository name (and optionally a tag) to be applied to the resulting image in case of success
976
+	:query q: suppress verbose build output
977
+    :query nocache: do not use the cache when building the image
978
+	:statuscode 200: no error
979
+    :statuscode 500: server error
980
+
981
+
982
+Check auth configuration
983
+************************
984
+
985
+.. http:post:: /auth
986
+
987
+        Get the default username and email
988
+
989
+        **Example request**:
990
+
991
+        .. sourcecode:: http
992
+
993
+           POST /auth HTTP/1.1
994
+	   Content-Type: application/json
995
+
996
+	   {
997
+		"username":"hannibal",
998
+		"password:"xxxx",
999
+		"email":"hannibal@a-team.com",
1000
+		"serveraddress":"https://index.docker.io/v1/"
1001
+	   }
1002
+
1003
+        **Example response**:
1004
+
1005
+        .. sourcecode:: http
1006
+
1007
+           HTTP/1.1 200 OK
1008
+
1009
+        :statuscode 200: no error
1010
+        :statuscode 204: no error
1011
+        :statuscode 500: server error
1012
+
1013
+
1014
+Display system-wide information
1015
+*******************************
1016
+
1017
+.. http:get:: /info
1018
+
1019
+	Display system-wide information
1020
+	
1021
+	**Example request**:
1022
+
1023
+        .. sourcecode:: http
1024
+
1025
+           GET /info HTTP/1.1
1026
+
1027
+        **Example response**:
1028
+
1029
+        .. sourcecode:: http
1030
+
1031
+           HTTP/1.1 200 OK
1032
+	   Content-Type: application/json
1033
+
1034
+	   {
1035
+		"Containers":11,
1036
+		"Images":16,
1037
+		"Debug":false,
1038
+		"NFd": 11,
1039
+		"NGoroutines":21,
1040
+		"MemoryLimit":true,
1041
+		"SwapLimit":false,
1042
+		"IPv4Forwarding":true
1043
+	   }
1044
+
1045
+        :statuscode 200: no error
1046
+        :statuscode 500: server error
1047
+
1048
+
1049
+Show the docker version information
1050
+***********************************
1051
+
1052
+.. http:get:: /version
1053
+
1054
+	Show the docker version information
1055
+
1056
+	**Example request**:
1057
+
1058
+        .. sourcecode:: http
1059
+
1060
+           GET /version HTTP/1.1
1061
+
1062
+        **Example response**:
1063
+
1064
+        .. sourcecode:: http
1065
+
1066
+           HTTP/1.1 200 OK
1067
+	   Content-Type: application/json
1068
+
1069
+	   {
1070
+		"Version":"0.2.2",
1071
+		"GitCommit":"5a2a5cc+CHANGES",
1072
+		"GoVersion":"go1.0.3"
1073
+	   }
1074
+
1075
+        :statuscode 200: no error
1076
+	:statuscode 500: server error
1077
+
1078
+
1079
+Create a new image from a container's changes
1080
+*********************************************
1081
+
1082
+.. http:post:: /commit
1083
+
1084
+    Create a new image from a container's changes
1085
+
1086
+    **Example request**:
1087
+
1088
+    .. sourcecode:: http
1089
+
1090
+        POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
1091
+
1092
+    **Example response**:
1093
+
1094
+    .. sourcecode:: http
1095
+
1096
+        HTTP/1.1 201 OK
1097
+	    Content-Type: application/vnd.docker.raw-stream
1098
+
1099
+        {"Id":"596069db4bf5"}
1100
+
1101
+    :query container: source container
1102
+    :query repo: repository
1103
+    :query tag: tag
1104
+    :query m: commit message
1105
+    :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
1106
+    :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
1107
+    :statuscode 201: no error
1108
+    :statuscode 404: no such container
1109
+    :statuscode 500: server error
1110
+
1111
+
1112
+Monitor Docker's events
1113
+***********************
1114
+
1115
+.. http:get:: /events
1116
+
1117
+	Get events from docker, either in real time via streaming, or via polling (using `since`)
1118
+
1119
+	**Example request**:
1120
+
1121
+	.. sourcecode:: http
1122
+
1123
+           POST /events?since=1374067924
1124
+
1125
+        **Example response**:
1126
+
1127
+        .. sourcecode:: http
1128
+
1129
+           HTTP/1.1 200 OK
1130
+	   Content-Type: application/json
1131
+
1132
+	   {"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
1133
+	   {"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
1134
+	   {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966}
1135
+	   {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970}
1136
+
1137
+	:query since: timestamp used for polling
1138
+        :statuscode 200: no error
1139
+        :statuscode 500: server error
1140
+
1141
+
1142
+3. Going further
1143
+================
1144
+
1145
+3.1 Inside 'docker run'
1146
+-----------------------
1147
+
1148
+Here are the steps of 'docker run' :
1149
+
1150
+* Create the container
1151
+* If the status code is 404, it means the image doesn't exists:
1152
+        * Try to pull it
1153
+        * Then retry to create the container
1154
+* Start the container
1155
+* If you are not in detached mode:
1156
+        * Attach to the container, using logs=1 (to have stdout and stderr from the container's start) and stream=1
1157
+* If in detached mode or only stdin is attached:
1158
+	* Display the container's id
1159
+
1160
+
1161
+3.2 Hijacking
1162
+-------------
1163
+
1164
+In this version of the API, /attach, uses hijacking to transport stdin, stdout and stderr on the same socket. This might change in the future.
1165
+
1166
+3.3 CORS Requests
1167
+-----------------
1168
+
1169
+To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode.
1170
+
1171
+.. code-block:: bash
1172
+
1173
+   docker -d -H="192.168.1.9:4243" -api-enable-cors
1174
+
... ...
@@ -32,11 +32,13 @@ Available Commands
32 32
    command/commit
33 33
    command/cp
34 34
    command/diff
35
+   command/events
35 36
    command/export
36 37
    command/history
37 38
    command/images
38 39
    command/import
39 40
    command/info
41
+   command/insert
40 42
    command/inspect
41 43
    command/kill
42 44
    command/login
43 45
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+:title: Events Command
1
+:description: Get real time events from the server
2
+:keywords: events, docker, documentation
3
+
4
+=================================================================
5
+``events`` -- Get real time events from the server
6
+=================================================================
7
+
8
+::
9
+
10
+    Usage: docker events
11
+
12
+    Get real time events from the server
13
+
14
+Examples
15
+--------
16
+
17
+Starting and stopping a container
18
+.................................
19
+
20
+.. code-block:: bash
21
+
22
+    $ sudo docker start 4386fb97867d
23
+    $ sudo docker stop 4386fb97867d
24
+
25
+In another shell
26
+
27
+.. code-block:: bash
28
+    
29
+    $ sudo docker events
30
+    [2013-09-03 15:49:26 +0200 CEST] 4386fb97867d: (from 12de384bfb10) start
31
+    [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) die
32
+    [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) stop
33
+
0 34
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+:title: Insert Command
1
+:description: Insert a file in an image
2
+:keywords: insert, image, docker, documentation
3
+
4
+==========================================================================
5
+``insert`` -- Insert a file in an image
6
+==========================================================================
7
+
8
+::
9
+
10
+    Usage: docker insert IMAGE URL PATH
11
+
12
+    Insert a file from URL in the IMAGE at PATH
13
+
14
+Examples
15
+--------
16
+
17
+Insert file from github
18
+.......................
19
+
20
+.. code-block:: bash
21
+
22
+    $ sudo docker insert 8283e18b24bc https://raw.github.com/metalivedev/django/master/postinstall /tmp/postinstall.sh
... ...
@@ -8,10 +8,17 @@
8 8
 
9 9
 ::
10 10
 
11
-    Usage: docker login [OPTIONS]
11
+    Usage: docker login [OPTIONS] [SERVER]
12 12
 
13 13
     Register or Login to the docker registry server
14 14
 
15 15
     -e="": email
16 16
     -p="": password
17 17
     -u="": username
18
+
19
+    If you want to login to a private registry you can
20
+    specify this by adding the server name.
21
+
22
+    example:
23
+    docker login localhost:8080
24
+
... ...
@@ -67,7 +67,7 @@ use-cases, like running Docker within Docker.
67 67
 
68 68
    docker  run -w /path/to/dir/ -i -t  ubuntu pwd
69 69
 
70
-The ``-w`` lets the command beeing executed inside directory given, 
70
+The ``-w`` lets the command being executed inside directory given, 
71 71
 here /path/to/dir/. If the path does not exists it is created inside the 
72 72
 container.
73 73
 
... ...
@@ -76,8 +76,8 @@ container.
76 76
    docker  run  -v `pwd`:`pwd` -w `pwd` -i -t  ubuntu pwd
77 77
 
78 78
 The ``-v`` flag mounts the current working directory into the container. 
79
-The ``-w`` lets the command beeing executed inside the current 
80
-working directory, by changeing into the directory to the value
79
+The ``-w`` lets the command being executed inside the current 
80
+working directory, by changing into the directory to the value
81 81
 returned by ``pwd``. So this combination executes the command
82 82
 using the container, but inside the current working directory.
83 83
 
... ...
@@ -17,11 +17,13 @@ Contents:
17 17
   commit  <command/commit>
18 18
   cp      <command/cp>
19 19
   diff    <command/diff>
20
+  events  <command/events>
20 21
   export  <command/export>
21 22
   history <command/history>
22 23
   images  <command/images>
23 24
   import  <command/import>
24 25
   info    <command/info>
26
+  insert  <command/insert>
25 27
   inspect <command/inspect>
26 28
   kill    <command/kill>
27 29
   login   <command/login>
... ...
@@ -2,6 +2,28 @@
2 2
 :description: A simple hello world example with Docker
3 3
 :keywords: docker, example, hello world
4 4
 
5
+.. _running_examples:
6
+
7
+Running the Examples
8
+====================
9
+
10
+All the examples assume your machine is running the docker daemon. To
11
+run the docker daemon in the background, simply type:
12
+
13
+.. code-block:: bash
14
+
15
+   sudo docker -d &
16
+
17
+Now you can run docker in client mode: by defalt all commands will be
18
+forwarded to the ``docker`` daemon via a protected Unix socket, so you
19
+must run as root.
20
+
21
+.. code-block:: bash
22
+
23
+   sudo docker help
24
+
25
+----
26
+
5 27
 .. _hello_world:
6 28
 
7 29
 Hello World
... ...
@@ -49,4 +71,108 @@ See the example in action
49 49
     </div>
50 50
 
51 51
 
52
-Continue to the :ref:`hello_world_daemon` example.
52
+----
53
+
54
+.. _hello_world_daemon:
55
+
56
+Hello World Daemon
57
+==================
58
+
59
+.. include:: example_header.inc
60
+
61
+And now for the most boring daemon ever written!
62
+
63
+This example assumes you have Docker installed and the Ubuntu
64
+image already imported with ``docker pull ubuntu``.  We will use the Ubuntu
65
+image to run a simple hello world daemon that will just print hello
66
+world to standard out every second. It will continue to do this until
67
+we stop it.
68
+
69
+**Steps:**
70
+
71
+.. code-block:: bash
72
+
73
+    CONTAINER_ID=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done")
74
+
75
+We are going to run a simple hello world daemon in a new container
76
+made from the *ubuntu* image.
77
+
78
+- **"docker run -d "** run a command in a new container. We pass "-d"
79
+  so it runs as a daemon.
80
+- **"ubuntu"** is the image we want to run the command inside of.
81
+- **"/bin/sh -c"** is the command we want to run in the container
82
+- **"while true; do echo hello world; sleep 1; done"** is the mini
83
+  script we want to run, that will just print hello world once a
84
+  second until we stop it.
85
+- **$CONTAINER_ID** the output of the run command will return a
86
+  container id, we can use in future commands to see what is going on
87
+  with this process.
88
+
89
+.. code-block:: bash
90
+
91
+    sudo docker logs $CONTAINER_ID
92
+
93
+Check the logs make sure it is working correctly.
94
+
95
+- **"docker logs**" This will return the logs for a container
96
+- **$CONTAINER_ID** The Id of the container we want the logs for.
97
+
98
+.. code-block:: bash
99
+
100
+    sudo docker attach $CONTAINER_ID
101
+
102
+Attach to the container to see the results in realtime.
103
+
104
+- **"docker attach**" This will allow us to attach to a background
105
+  process to see what is going on.
106
+- **$CONTAINER_ID** The Id of the container we want to attach too.
107
+
108
+Exit from the container attachment by pressing Control-C.
109
+
110
+.. code-block:: bash
111
+
112
+    sudo docker ps
113
+
114
+Check the process list to make sure it is running.
115
+
116
+- **"docker ps"** this shows all running process managed by docker
117
+
118
+.. code-block:: bash
119
+
120
+    sudo docker stop $CONTAINER_ID
121
+
122
+Stop the container, since we don't need it anymore.
123
+
124
+- **"docker stop"** This stops a container
125
+- **$CONTAINER_ID** The Id of the container we want to stop.
126
+
127
+.. code-block:: bash
128
+
129
+    sudo docker ps
130
+
131
+Make sure it is really stopped.
132
+
133
+
134
+**Video:**
135
+
136
+See the example in action
137
+
138
+.. raw:: html
139
+
140
+    <div style="margin-top:10px;">
141
+      <iframe width="560" height="350" src="http://ascii.io/a/2562/raw" frameborder="0"></iframe>
142
+    </div>
143
+
144
+The next example in the series is a :ref:`python_web_app` example, or
145
+you could skip to any of the other examples:
146
+
147
+.. toctree::
148
+   :maxdepth: 1
149
+
150
+   python_web_app
151
+   nodejs_web_app
152
+   running_redis_service
153
+   running_ssh_service
154
+   couchdb_data_volumes
155
+   postgresql_service
156
+   mongodb
53 157
deleted file mode 100644
... ...
@@ -1,93 +0,0 @@
1
-:title: Hello world daemon example
2
-:description: A simple hello world daemon example with Docker
3
-:keywords: docker, example, hello world, daemon
4
-
5
-.. _hello_world_daemon:
6
-
7
-Hello World Daemon
8
-==================
9
-
10
-.. include:: example_header.inc
11
-
12
-The most boring daemon ever written.
13
-
14
-This example assumes you have Docker installed and with the Ubuntu
15
-image already imported ``docker pull ubuntu``.  We will use the Ubuntu
16
-image to run a simple hello world daemon that will just print hello
17
-world to standard out every second. It will continue to do this until
18
-we stop it.
19
-
20
-**Steps:**
21
-
22
-.. code-block:: bash
23
-
24
-    CONTAINER_ID=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done")
25
-
26
-We are going to run a simple hello world daemon in a new container
27
-made from the *ubuntu* image.
28
-
29
-- **"docker run -d "** run a command in a new container. We pass "-d"
30
-  so it runs as a daemon.
31
-- **"ubuntu"** is the image we want to run the command inside of.
32
-- **"/bin/sh -c"** is the command we want to run in the container
33
-- **"while true; do echo hello world; sleep 1; done"** is the mini
34
-  script we want to run, that will just print hello world once a
35
-  second until we stop it.
36
-- **$CONTAINER_ID** the output of the run command will return a
37
-  container id, we can use in future commands to see what is going on
38
-  with this process.
39
-
40
-.. code-block:: bash
41
-
42
-    sudo docker logs $CONTAINER_ID
43
-
44
-Check the logs make sure it is working correctly.
45
-
46
-- **"docker logs**" This will return the logs for a container
47
-- **$CONTAINER_ID** The Id of the container we want the logs for.
48
-
49
-.. code-block:: bash
50
-
51
-    sudo docker attach $CONTAINER_ID
52
-
53
-Attach to the container to see the results in realtime.
54
-
55
-- **"docker attach**" This will allow us to attach to a background
56
-  process to see what is going on.
57
-- **$CONTAINER_ID** The Id of the container we want to attach too.
58
-
59
-.. code-block:: bash
60
-
61
-    sudo docker ps
62
-
63
-Check the process list to make sure it is running.
64
-
65
-- **"docker ps"** this shows all running process managed by docker
66
-
67
-.. code-block:: bash
68
-
69
-    sudo docker stop $CONTAINER_ID
70
-
71
-Stop the container, since we don't need it anymore.
72
-
73
-- **"docker stop"** This stops a container
74
-- **$CONTAINER_ID** The Id of the container we want to stop.
75
-
76
-.. code-block:: bash
77
-
78
-    sudo docker ps
79
-
80
-Make sure it is really stopped.
81
-
82
-
83
-**Video:**
84
-
85
-See the example in action
86
-
87
-.. raw:: html
88
-
89
-    <div style="margin-top:10px;">
90
-      <iframe width="560" height="350" src="http://ascii.io/a/2562/raw" frameborder="0"></iframe>
91
-    </div>
92
-
93
-Continue to the :ref:`python_web_app` example.
... ...
@@ -5,16 +5,16 @@
5 5
 
6 6
 
7 7
 Examples
8
-============
8
+========
9 9
 
10
-Contents:
10
+Here are some examples of how to use Docker to create running
11
+processes, starting from a very simple *Hello World* and progressing
12
+to more substantial services like you might find in production.
11 13
 
12 14
 .. toctree::
13 15
    :maxdepth: 1
14 16
 
15
-   running_examples
16 17
    hello_world
17
-   hello_world_daemon
18 18
    python_web_app
19 19
    nodejs_web_app
20 20
    running_redis_service
... ...
@@ -22,3 +22,4 @@ Contents:
22 22
    couchdb_data_volumes
23 23
    postgresql_service
24 24
    mongodb
25
+   running_riak_service
... ...
@@ -86,7 +86,7 @@ http://0.0.0.0:5000/" in the log output.
86 86
 
87 87
 .. code-block:: bash
88 88
 
89
-    WEB_PORT=$(docker port $WEB_WORKER 5000)
89
+    WEB_PORT=$(sudo docker port $WEB_WORKER 5000)
90 90
 
91 91
 Look up the public-facing port which is NAT-ed. Find the private port
92 92
 used by the container and store it inside of the WEB_PORT variable.
93 93
deleted file mode 100644
... ...
@@ -1,23 +0,0 @@
1
-:title: Running the Examples
2
-:description: An overview on how to run the docker examples
3
-:keywords: docker, examples, how to
4
-
5
-.. _running_examples:
6
-
7
-Running the Examples
8
-
9
-All the examples assume your machine is running the docker daemon. To
10
-run the docker daemon in the background, simply type:
11
-
12
-   .. code-block:: bash
13
-
14
-      sudo docker -d &
15
-
16
-Now you can run docker in client mode: by defalt all commands will be
17
-forwarded to the ``docker`` daemon via a protected Unix socket, so you
18
-must run as root.
19
-
20
-   .. code-block:: bash
21
-
22
-      sudo docker help
23 1
new file mode 100644
... ...
@@ -0,0 +1,151 @@
0
+:title: Running a Riak service
1
+:description: Build a Docker image with Riak pre-installed
2
+:keywords: docker, example, package installation, networking, riak
3
+
4
+Riak Service
5
+==============================
6
+
7
+.. include:: example_header.inc
8
+
9
+The goal of this example is to show you how to build a Docker image with Riak
10
+pre-installed.
11
+
12
+Creating a ``Dockerfile``
13
+
14
+Create an empty file called ``Dockerfile``:
15
+
16
+.. code-block:: bash
17
+
18
+    touch Dockerfile
19
+
20
+Next, define the parent image you want to use to build your image on top of.
21
+We’ll use `Ubuntu <https://index.docker.io/_/ubuntu/>`_ (tag: ``latest``),
22
+which is available on the `docker index <http://index.docker.io>`_:
23
+
24
+.. code-block:: bash
25
+
26
+    # Riak
27
+    #
28
+    # VERSION       0.1.0
29
+
30
+    # Use the Ubuntu base image provided by dotCloud
31
+    FROM ubuntu:latest
32
+    MAINTAINER Hector Castro hector@basho.com
33
+
34
+Next, we update the APT cache and apply any updates:
35
+
36
+.. code-block:: bash
37
+
38
+    # Update the APT cache
39
+    RUN sed -i.bak 's/main$/main universe/' /etc/apt/sources.list
40
+    RUN apt-get update
41
+    RUN apt-get upgrade -y
42
+
43
+After that, we install and setup a few dependencies:
44
+
45
+- ``curl`` is used to download Basho's APT repository key
46
+- ``lsb-release`` helps us derive the Ubuntu release codename
47
+- ``openssh-server`` allows us to login to containers remotely and join Riak
48
+  nodes to form a cluster
49
+- ``supervisor`` is used manage the OpenSSH and Riak processes
50
+
51
+.. code-block:: bash
52
+
53
+    # Install and setup project dependencies
54
+    RUN apt-get install -y curl lsb-release supervisor openssh-server
55
+
56
+    RUN mkdir -p /var/run/sshd
57
+    RUN mkdir -p /var/log/supervisor
58
+
59
+    RUN locale-gen en_US en_US.UTF-8
60
+
61
+    ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf
62
+
63
+    RUN echo 'root:basho' | chpasswd
64
+
65
+Next, we add Basho's APT repository:
66
+
67
+.. code-block:: bash
68
+
69
+    RUN curl -s http://apt.basho.com/gpg/basho.apt.key | apt-key add --
70
+    RUN echo "deb http://apt.basho.com $(lsb_release -cs) main" > /etc/apt/sources.list.d/basho.list
71
+    RUN apt-get update
72
+
73
+After that, we install Riak and alter a few defaults:
74
+
75
+.. code-block:: bash
76
+
77
+    # Install Riak and prepare it to run
78
+    RUN apt-get install -y riak
79
+    RUN sed -i.bak 's/127.0.0.1/0.0.0.0/' /etc/riak/app.config
80
+    RUN echo "ulimit -n 4096" >> /etc/default/riak
81
+
82
+Almost there. Next, we add a hack to get us by the lack of ``initctl``:
83
+
84
+.. code-block:: bash
85
+
86
+    # Hack for initctl
87
+    # See: https://github.com/dotcloud/docker/issues/1024
88
+    RUN dpkg-divert --local --rename --add /sbin/initctl
89
+    RUN ln -s /bin/true /sbin/initctl
90
+
91
+Then, we expose the Riak Protocol Buffers and HTTP interfaces, along with SSH:
92
+
93
+.. code-block:: bash
94
+
95
+    # Expose Riak Protocol Buffers and HTTP interfaces, along with SSH
96
+    EXPOSE 8087 8098 22
97
+
98
+Finally, run ``supervisord`` so that Riak and OpenSSH are started:
99
+
100
+.. code-block:: bash
101
+
102
+    CMD ["/usr/bin/supervisord"]
103
+
104
+Create a ``supervisord`` configuration file
105
+
106
+Create an empty file called ``supervisord.conf``. Make sure it's at the same
107
+level as your ``Dockerfile``:
108
+
109
+.. code-block:: bash
110
+
111
+    touch supervisord.conf
112
+
113
+Populate it with the following program definitions:
114
+
115
+.. code-block:: bash
116
+
117
+    [supervisord]
118
+    nodaemon=true
119
+
120
+    [program:sshd]
121
+    command=/usr/sbin/sshd -D
122
+    stdout_logfile=/var/log/supervisor/%(program_name)s.log
123
+    stderr_logfile=/var/log/supervisor/%(program_name)s.log
124
+    autorestart=true
125
+
126
+    [program:riak]
127
+    command=bash -c ". /etc/default/riak && /usr/sbin/riak console"
128
+    pidfile=/var/log/riak/riak.pid
129
+    stdout_logfile=/var/log/supervisor/%(program_name)s.log
130
+    stderr_logfile=/var/log/supervisor/%(program_name)s.log
131
+
132
+Build the Docker image for Riak
133
+
134
+Now you should be able to build a Docker image for Riak:
135
+
136
+.. code-block:: bash
137
+
138
+    docker build -t "<yourname>/riak" .
139
+
140
+Next steps
141
+
142
+Riak is a distributed database. Many production deployments consist of `at
143
+least five nodes <http://basho.com/why-your-riak-cluster-should-have-at-least-
144
+five-nodes/>`_. See the `docker-riak <https://github.com/hectcastro /docker-
145
+riak>`_ project details on how to deploy a Riak cluster using Docker and
146
+Pipework.
... ...
@@ -5,18 +5,20 @@
5 5
 Using Vagrant (Amazon EC2)
6 6
 ==========================
7 7
 
8
-This page explains how to setup and run an Amazon EC2 instance from your local machine. 
9
-Vagrant is not necessary to run Docker on EC2. You can follow the :ref:`ubuntu_linux` instructions
10
-installing Docker on any EC2 instance running Ubuntu
8
+This page explains how to setup and run an Amazon EC2 instance from
9
+your local machine.  **Vagrant is not necessary to run Docker on
10
+EC2.** You can follow the :ref:`ubuntu_linux` instructions installing
11
+Docker on any EC2 instance running Ubuntu.
11 12
 
12
-  Please note this is a community contributed installation path. The only 'official' installation is using the
13
-  :ref:`ubuntu_linux` installation path. This version may sometimes be out of date.
14
-  
15
-  
16 13
 Installation
17 14
 ------------
18 15
 
19
-Docker can now be installed on Amazon EC2 with a single vagrant command. Vagrant 1.1 or higher is required.
16
+.. include:: install_header.inc
17
+
18
+.. include:: install_unofficial.inc
19
+  
20
+Docker can now be installed on Amazon EC2 with a single vagrant
21
+command. Vagrant 1.1 or higher is required.
20 22
 
21 23
 1. Install vagrant from http://www.vagrantup.com/ (or use your package manager)
22 24
 2. Install the vagrant aws plugin
... ...
@@ -7,10 +7,6 @@
7 7
 Arch Linux
8 8
 ==========
9 9
 
10
-  Please note this is a community contributed installation path. The only 'official' installation is using the
11
-  :ref:`ubuntu_linux` installation path. This version may sometimes be out of date.
12
-
13
-
14 10
 Installing on Arch Linux is not officially supported but can be handled via 
15 11
 either of the following AUR packages:
16 12
 
... ...
@@ -36,6 +32,10 @@ either AUR package.
36 36
 Installation
37 37
 ------------
38 38
 
39
+.. include:: install_header.inc
40
+
41
+.. include:: install_unofficial.inc
42
+
39 43
 The instructions here assume **yaourt** is installed.  See 
40 44
 `Arch User Repository <https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages>`_
41 45
 for information on building and installing packages from the AUR if you have not
... ...
@@ -7,9 +7,10 @@
7 7
 Binaries
8 8
 ========
9 9
 
10
-  **Please note this project is currently under heavy development. It should not be used in production.**
10
+.. include:: install_header.inc
11 11
 
12
-**This instruction set is meant for hackers who want to try out Docker on a variety of environments.**
12
+**This instruction set is meant for hackers who want to try out Docker
13
+on a variety of environments.**
13 14
 
14 15
 Right now, the officially supported distributions are:
15 16
 
... ...
@@ -23,22 +24,18 @@ But we know people have had success running it under
23 23
 - Suse
24 24
 - :ref:`arch_linux`
25 25
 
26
+Check Your Kernel
27
+-----------------
26 28
 
27
-Dependencies:
28
-
29
-* 3.8 Kernel (read more about :ref:`kernel`)
30
-* AUFS filesystem support
31
-* lxc
32
-* xz-utils
29
+Your host's Linux kernel must meet the Docker :ref:`kernel`
33 30
 
34 31
 Get the docker binary:
35 32
 ----------------------
36 33
 
37 34
 .. code-block:: bash
38 35
 
39
-    wget http://get.docker.io/builds/Linux/x86_64/docker-latest.tgz
40
-    tar -xf docker-latest.tgz
36
+    wget --output-document=docker https://get.docker.io/builds/Linux/x86_64/docker-latest
37
+    chmod +x docker
41 38
 
42 39
 
43 40
 Run the docker daemon
44 41
new file mode 100644
... ...
@@ -0,0 +1,125 @@
0
+:title: Installation on Gentoo Linux
1
+:description: Docker installation instructions and nuances for Gentoo Linux.
2
+:keywords: gentoo linux, virtualization, docker, documentation, installation
3
+
4
+.. _gentoo_linux:
5
+
6
+Gentoo Linux
7
+============
8
+
9
+.. include:: install_header.inc
10
+
11
+.. include:: install_unofficial.inc
12
+
13
+Installing Docker on Gentoo Linux can be accomplished by using the overlay
14
+provided at https://github.com/tianon/docker-overlay.  The most up-to-date
15
+documentation for properly installing the overlay can be found in the overlay
16
+README.  The information here is provided for reference, and may be out of date.
17
+
18
+Installation
19
+^^^^^^^^^^^^
20
+
21
+Ensure that layman is installed:
22
+
23
+.. code-block:: bash
24
+
25
+   sudo emerge -av app-portage/layman
26
+
27
+Using your favorite editor, add
28
+``https://raw.github.com/tianon/docker-overlay/master/repositories.xml`` to the
29
+``overlays`` section in ``/etc/layman/layman.cfg`` (as per instructions on the
30
+`Gentoo Wiki <http://wiki.gentoo.org/wiki/Layman#Adding_custom_overlays>`_),
31
+then invoke the following:
32
+
33
+.. code-block:: bash
34
+
35
+   sudo layman -f -a docker
36
+
37
+Once that completes, the ``app-emulation/docker`` package will be available
38
+for emerge:
39
+
40
+.. code-block:: bash
41
+
42
+   sudo emerge -av app-emulation/docker
43
+
44
+If you prefer to use the official binaries, or just do not wish to compile
45
+docker, emerge ``app-emulation/docker-bin`` instead.  It is important to
46
+remember that Gentoo is still an unsupported platform, even when using the
47
+official binaries.
48
+
49
+The package should already include all the necessary dependencies.  For the
50
+simplest installation experience, use ``sys-kernel/aufs-sources`` directly as
51
+your kernel sources.  If you prefer not to use ``sys-kernel/aufs-sources``, the
52
+portage tree also contains ``sys-fs/aufs3``, which contains the patches
53
+necessary for adding AUFS support to other kernel source packages (and a
54
+``kernel-patch`` use flag to perform the patching automatically).
55
+
56
+Between ``app-emulation/lxc`` and ``app-emulation/docker``, all the
57
+necessary kernel configuration flags should be checked for and warned about in
58
+the standard manner.
59
+
60
+If any issues arise from this ebuild or the resulting binary, including and
61
+especially missing kernel configuration flags and/or dependencies, `open an
62
+issue <https://github.com/tianon/docker-overlay/issues>`_ on the docker-overlay
63
+repository or ping tianon in the #docker IRC channel.
64
+
65
+Starting Docker
66
+^^^^^^^^^^^^^^^
67
+
68
+Ensure that you are running a kernel that includes the necessary AUFS support
69
+and includes all the necessary modules and/or configuration for LXC.
70
+
71
+OpenRC
72
+------
73
+
74
+To start the docker daemon:
75
+
76
+.. code-block:: bash
77
+
78
+   sudo /etc/init.d/docker start
79
+
80
+To start on system boot:
81
+
82
+.. code-block:: bash
83
+
84
+   sudo rc-update add docker default
85
+
86
+systemd
87
+-------
88
+
89
+To start the docker daemon:
90
+
91
+.. code-block:: bash
92
+
93
+   sudo systemctl start docker.service
94
+
95
+To start on system boot:
96
+
97
+.. code-block:: bash
98
+
99
+   sudo systemctl enable docker.service
100
+
101
+Network Configuration
102
+^^^^^^^^^^^^^^^^^^^^^
103
+
104
+IPv4 packet forwarding is disabled by default, so internet access from inside
105
+the container will not work unless ``net.ipv4.ip_forward`` is enabled:
106
+
107
+.. code-block:: bash
108
+
109
+   sudo sysctl -w net.ipv4.ip_forward=1
110
+
111
+Or, to enable it more permanently:
112
+
113
+.. code-block:: bash
114
+
115
+   echo net.ipv4.ip_forward = 1 | sudo tee /etc/sysctl.d/docker.conf
116
+
117
+fork/exec /usr/sbin/lxc-start: operation not permitted
118
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
119
+
120
+Unfortunately, Gentoo suffers from `issue #1422
121
+<https://github.com/dotcloud/docker/issues/1422>`_, meaning that after every
122
+fresh start of docker, the first docker run fails due to some tricky terminal
123
+issues, so be sure to run something trivial (such as ``docker run -i -t busybox
124
+echo hi``) before attempting to run anything important.
... ...
@@ -24,5 +24,6 @@ Contents:
24 24
    amazon
25 25
    rackspace
26 26
    archlinux
27
+   gentoolinux
27 28
    upgrading
28 29
    kernel
29 30
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+
1
+.. note::
2
+
3
+   Docker is still under heavy development! We don't recommend using
4
+   it in production yet, but we're getting closer with each
5
+   release. Please see our blog post, `"Getting to Docker 1.0"
6
+   <http://blog.docker.io/2013/08/getting-to-docker-1-0/>`_
0 7
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+
1
+.. note::
2
+
3
+   This is a community contributed installation path. The only
4
+   'official' installation is using the :ref:`ubuntu_linux`
5
+   installation path. This version may be out of date because it
6
+   depends on some binaries to be updated and published
... ...
@@ -6,21 +6,22 @@
6 6
 Rackspace Cloud
7 7
 ===============
8 8
 
9
-  Please note this is a community contributed installation path. The only 'official' installation is using the
10
-  :ref:`ubuntu_linux` installation path. This version may sometimes be out of date.
9
+.. include:: install_unofficial.inc
11 10
 
12
-
13
-Installing Docker on Ubuntu provided by Rackspace is pretty straightforward, and you should mostly be able to follow the
11
+Installing Docker on Ubuntu provided by Rackspace is pretty
12
+straightforward, and you should mostly be able to follow the
14 13
 :ref:`ubuntu_linux` installation guide.
15 14
 
16 15
 **However, there is one caveat:**
17 16
 
18
-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
19
-more difficult on Rackspace.
17
+If you are using any linux not already shipping with the 3.8 kernel
18
+you will need to install it. And this is a little more difficult on
19
+Rackspace.
20 20
 
21
-Rackspace boots their servers using grub's menu.lst and does not like non 'virtual' packages (e.g. xen compatible)
22
-kernels there, although they do work. This makes ``update-grub`` to not have the expected result, and you need to
23
-set the kernel manually.
21
+Rackspace boots their servers using grub's ``menu.lst`` and does not
22
+like non 'virtual' packages (e.g. xen compatible) kernels there,
23
+although they do work. This makes ``update-grub`` to not have the
24
+expected result, and you need to set the kernel manually.
24 25
 
25 26
 **Do not attempt this on a production machine!**
26 27
 
... ...
@@ -33,7 +34,8 @@ set the kernel manually.
33 33
     apt-get install linux-generic-lts-raring
34 34
 
35 35
 
36
-Great, now you have kernel installed in /boot/, next is to make it boot next time.
36
+Great, now you have kernel installed in ``/boot/``, next is to make it
37
+boot next time.
37 38
 
38 39
 .. code-block:: bash
39 40
 
... ...
@@ -43,9 +45,10 @@ Great, now you have kernel installed in /boot/, next is to make it boot next tim
43 43
     # this should return some results
44 44
 
45 45
 
46
-Now you need to manually edit /boot/grub/menu.lst, you will find a section at the bottom with the existing options.
47
-Copy the top one and substitute the new kernel into that. Make sure the new kernel is on top, and double check kernel
48
-and initrd point to the right files.
46
+Now you need to manually edit ``/boot/grub/menu.lst``, you will find a
47
+section at the bottom with the existing options.  Copy the top one and
48
+substitute the new kernel into that. Make sure the new kernel is on
49
+top, and double check kernel and initrd point to the right files.
49 50
 
50 51
 Make special care to double check the kernel and initrd entries.
51 52
 
... ...
@@ -92,4 +95,4 @@ Verify the kernel was updated
92 92
     # nice! 3.8.
93 93
 
94 94
 
95
-Now you can finish with the :ref:`ubuntu_linux` instructions.
96 95
\ No newline at end of file
96
+Now you can finish with the :ref:`ubuntu_linux` instructions.
... ...
@@ -2,15 +2,17 @@
2 2
 :description: Please note this project is currently under heavy development. It should not be used in production.
3 3
 :keywords: Docker, Docker documentation, requirements, virtualbox, vagrant, git, ssh, putty, cygwin, linux
4 4
 
5
-**These instructions have changed for 0.6. If you are upgrading from an earlier version, you will need to follow them again.**
6
-
7 5
 .. _ubuntu_linux:
8 6
 
9 7
 Ubuntu Linux
10 8
 ============
11 9
 
12
-   **Please note this project is currently under heavy development. It should not be used in production.**
10
+.. warning::
11
+
12
+   These instructions have changed for 0.6. If you are upgrading from
13
+   an earlier version, you will need to follow them again.
13 14
 
15
+.. include:: install_header.inc
14 16
 
15 17
 Right now, the officially supported distribution are:
16 18
 
... ...
@@ -22,7 +24,8 @@ Docker has the following dependencies
22 22
 * Linux kernel 3.8 (read more about :ref:`kernel`)
23 23
 * AUFS file system support (we are working on BTRFS support as an alternative)
24 24
 
25
-Please read :ref:`ufw`, if you plan to use `UFW (Uncomplicated Firewall) <https://help.ubuntu.com/community/UFW>`_
25
+Please read :ref:`ufw`, if you plan to use `UFW (Uncomplicated
26
+Firewall) <https://help.ubuntu.com/community/UFW>`_
26 27
 
27 28
 .. _ubuntu_precise:
28 29
 
... ...
@@ -38,12 +41,13 @@ Dependencies
38 38
 **Linux kernel 3.8**
39 39
 
40 40
 Due to a bug in LXC, docker works best on the 3.8 kernel. Precise
41
-comes with a 3.2 kernel, so we need to upgrade it. The kernel you'll install when following these steps
42
-comes with AUFS built in. We also include the generic headers
43
-to enable packages that depend on them, like ZFS and the VirtualBox
44
-guest additions. If you didn't install the headers for your "precise"
45
-kernel, then you can skip these headers for the "raring" kernel. But
46
-it is safer to include them if you're not sure.
41
+comes with a 3.2 kernel, so we need to upgrade it. The kernel you'll
42
+install when following these steps comes with AUFS built in. We also
43
+include the generic headers to enable packages that depend on them,
44
+like ZFS and the VirtualBox guest additions. If you didn't install the
45
+headers for your "precise" kernel, then you can skip these headers for
46
+the "raring" kernel. But it is safer to include them if you're not
47
+sure.
47 48
 
48 49
 
49 50
 .. code-block:: bash
... ...
@@ -59,14 +63,18 @@ it is safer to include them if you're not sure.
59 59
 Installation
60 60
 ------------
61 61
 
62
+.. warning::
63
+
64
+   These instructions have changed for 0.6. If you are upgrading from
65
+   an earlier version, you will need to follow them again.
66
+
62 67
 Docker is available as a Debian package, which makes installation easy.
63 68
 
64
-*Please note that these instructions have changed for 0.6. If you are upgrading from an earlier version, you will need
65
-to follow them again.*
66 69
 
67 70
 .. code-block:: bash
68 71
 
69 72
    # Add the Docker repository key to your local keychain
73
+   # using apt-key finger you can check the fingerprint matches 36A1 D786 9245 C895 0F96 6E92 D857 6A8B A88D 21E9
70 74
    sudo sh -c "curl https://get.docker.io/gpg | apt-key add -"
71 75
 
72 76
    # Add the Docker repository to your apt sources list.
... ...
@@ -120,6 +128,7 @@ to follow them again.*
120 120
 .. code-block:: bash
121 121
 
122 122
    # Add the Docker repository key to your local keychain
123
+   # using apt-key finger you can check the fingerprint matches 36A1 D786 9245 C895 0F96 6E92 D857 6A8B A88D 21E9
123 124
    sudo sh -c "curl http://get.docker.io/gpg | apt-key add -"
124 125
 
125 126
    # Add the Docker repository to your apt sources list.
... ...
@@ -136,7 +145,8 @@ Verify it worked
136 136
 
137 137
 .. code-block:: bash
138 138
 
139
-   # download the base 'ubuntu' container and run bash inside it while setting up an interactive shell
139
+   # download the base 'ubuntu' container
140
+   # and run bash inside it while setting up an interactive shell
140 141
    sudo docker run -i -t ubuntu /bin/bash
141 142
 
142 143
    # type exit to exit
... ...
@@ -150,7 +160,8 @@ Verify it worked
150 150
 Docker and UFW
151 151
 ^^^^^^^^^^^^^^
152 152
 
153
-Docker uses a bridge to manage containers networking, by default UFW drop all `forwarding`, a first step is to enable forwarding:
153
+Docker uses a bridge to manage containers networking, by default UFW
154
+drop all `forwarding`, a first step is to enable forwarding:
154 155
 
155 156
 .. code-block:: bash
156 157
 
... ...
@@ -168,8 +179,9 @@ Then reload UFW:
168 168
    sudo ufw reload
169 169
 
170 170
 
171
-UFW's default set of rules denied all `incoming`, so if you want to be able to reach your containers from another host,
172
-you should allow incoming connections on the docker port (default 4243):
171
+UFW's default set of rules denied all `incoming`, so if you want to be
172
+able to reach your containers from another host, you should allow
173
+incoming connections on the docker port (default 4243):
173 174
 
174 175
 .. code-block:: bash
175 176
 
... ...
@@ -5,18 +5,32 @@
5 5
 .. _upgrading:
6 6
 
7 7
 Upgrading
8
-============
8
+=========
9 9
 
10
-**These instructions are for upgrading Docker**
10
+The technique for upgrading ``docker`` to a newer version depends on
11
+how you installed ``docker``.
11 12
 
13
+.. versionadded:: 0.5.3
14
+   You may wish to add a ``docker`` group to your system to avoid using sudo with ``docker``. (see :ref:`dockergroup`)
12 15
 
13
-After normal installation
14 16
 
15
-If you installed Docker normally using apt-get or used Vagrant, use apt-get to upgrade.
17
+After ``apt-get``
18
+-----------------
19
+
20
+If you installed Docker using ``apt-get`` or Vagrant, then you should
21
+use ``apt-get`` to upgrade.
22
+
23
+.. versionadded:: 0.6
24
+   Add Docker repository information to your system first.
16 25
 
17 26
 .. code-block:: bash
18 27
 
28
+   # Add the Docker repository key to your local keychain
29
+   sudo sh -c "curl https://get.docker.io/gpg | apt-key add -"
30
+
31
+   # Add the Docker repository to your apt sources list.
32
+   sudo sh -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
33
+
19 34
    # update your sources list
20 35
    sudo apt-get update
21 36
 
... ...
@@ -27,7 +41,7 @@ If you installed Docker normally using apt-get or used Vagrant, use apt-get to u
27 27
 After manual installation
28 28
 -------------------------
29 29
 
30
-If you installed the Docker binary
30
+If you installed the Docker :ref:`binaries` then follow these steps:
31 31
 
32 32
 
33 33
 .. code-block:: bash
... ...
@@ -48,8 +62,10 @@ If you installed the Docker binary
48 48
    tar -xf docker-latest.tgz
49 49
 
50 50
 
51
-Start docker in daemon mode (-d) and disconnect (&) starting ./docker will start the version in your current dir rather than a version which
52
-might reside in your path.
51
+Start docker in daemon mode (``-d``) and disconnect, running the
52
+daemon in the background (``&``). Starting as ``./docker`` guarantees
53
+to run the version in your current directory rather than a version
54
+which might reside in your path.
53 55
 
54 56
 .. code-block:: bash
55 57
 
... ...
@@ -2,37 +2,48 @@
2 2
 :description: This guide will setup a new virtualbox virtual machine with docker installed on your computer.
3 3
 :keywords: Docker, Docker documentation, virtualbox, vagrant, git, ssh, putty, cygwin
4 4
 
5
-**Vagrant installation is temporarily out of date, it will be updated for 0.6 soon.**
6
-
7 5
 .. _install_using_vagrant:
8 6
 
9 7
 Using Vagrant (Mac, Linux)
10 8
 ==========================
11 9
 
12
-This guide will setup a new virtualbox virtual machine with docker installed on your computer. This works on most operating
13
-systems, including MacOX, Windows, Linux, FreeBSD and others. If you can install these and have at least 400Mb RAM
14
-to spare you should be good.
15
-
10
+This guide will setup a new virtualbox virtual machine with docker
11
+installed on your computer. This works on most operating systems,
12
+including MacOX, Windows, Linux, FreeBSD and others. If you can
13
+install these and have at least 400MB RAM to spare you should be good.
16 14
 
17 15
 Install Vagrant and Virtualbox
18 16
 ------------------------------
19 17
 
20
-1. Install virtualbox from https://www.virtualbox.org/ (or use your package manager)
21
-2. Install vagrant from http://www.vagrantup.com/ (or use your package manager)
22
-3. Install git if you had not installed it before, check if it is installed by running
23
-   ``git`` in a terminal window
18
+.. include:: install_header.inc
19
+
20
+.. include:: install_unofficial.inc
21
+
22
+#. Install virtualbox from https://www.virtualbox.org/ (or use your
23
+   package manager)
24
+#. Install vagrant from http://www.vagrantup.com/ (or use your package
25
+   manager)
26
+#. Install git if you had not installed it before, check if it is
27
+   installed by running ``git`` in a terminal window
24 28
 
25 29
 
26 30
 Spin it up
27 31
 ----------
28 32
 
29
-1. Fetch the docker sources (this includes the Vagrantfile for machine setup).
33
+1. Fetch the docker sources (this includes the ``Vagrantfile`` for
34
+   machine setup).
30 35
 
31 36
    .. code-block:: bash
32 37
 
33 38
       git clone https://github.com/dotcloud/docker.git
34 39
 
35
-2. Run vagrant from the sources directory
40
+2. Change directory to docker
41
+
42
+   .. code-block:: bash
43
+
44
+      cd docker
45
+
46
+3. Run vagrant from the sources directory
36 47
 
37 48
    .. code-block:: bash
38 49
 
... ...
@@ -2,20 +2,20 @@
2 2
 :description: Docker's tutorial to run docker on Windows
3 3
 :keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
4 4
 
5
-**Vagrant installation is temporarily out of date, it will be updated for 0.6 soon.**
6
-
7 5
 .. _windows:
8 6
 
9 7
 Using Vagrant (Windows)
10 8
 =======================
11 9
 
12
-  Please note this is a community contributed installation path. The only 'official' installation is using the :ref:`ubuntu_linux` installation path. This version
13
-  may be out of date because it depends on some binaries to be updated and published
10
+Docker can run on Windows using a VM like VirtualBox. You then run
11
+Linux within the VM.
14 12
 
13
+Installation
14
+------------
15 15
 
16
+.. include:: install_header.inc
16 17
 
17
-Requirements
18
+.. include:: install_unofficial.inc
18 19
 
19 20
 1. Install virtualbox from https://www.virtualbox.org - or follow this tutorial__
20 21
 
... ...
@@ -35,7 +35,10 @@ We recommend having at least 2Gb of free disk space and 2Gb of RAM (or more).
35 35
 Opening a command prompt
36 36
 ------------------------
37 37
 
38
-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.
38
+First open a cmd prompt. Press Windows key and then press “R”
39
+key. This will open the RUN dialog box for you. Type “cmd” and press
40
+Enter. Or you can click on Start, type “cmd” in the “Search programs
41
+and files” field, and click on cmd.exe.
39 42
 
40 43
 .. image:: images/win/_01.gif
41 44
    :alt: Git install
... ...
@@ -47,14 +50,17 @@ This should open a cmd prompt window.
47 47
    :alt: run docker
48 48
    :align: center
49 49
 
50
-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.
50
+Alternatively, you can also use a Cygwin terminal, or Git Bash (or any
51
+other command line program you are usually using). The next steps
52
+would be the same.
51 53
 
52 54
 .. _launch_ubuntu:
53 55
 
54 56
 Launch an Ubuntu virtual server
55 57
 -------------------------------
56 58
 
57
-Let’s download and run an Ubuntu image with docker binaries already installed.
59
+Let’s download and run an Ubuntu image with docker binaries already
60
+installed.
58 61
 
59 62
 .. code-block:: bash
60 63
 
... ...
@@ -66,7 +72,9 @@ Let’s download and run an Ubuntu image with docker binaries already installed.
66 66
    :alt: run docker
67 67
    :align: center
68 68
 
69
-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.
69
+Congratulations! You are running an Ubuntu server with docker
70
+installed on it. You do not see it though, because it is running in
71
+the background.
70 72
 
71 73
 Log onto your Ubuntu server
72 74
 ---------------------------
... ...
@@ -85,7 +93,12 @@ Run the following command
85 85
 
86 86
 	vagrant ssh
87 87
 
88
-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:
88
+You may see an error message starting with “`ssh` executable not
89
+found”. In this case it means that you do not have SSH in your
90
+PATH. If you do not have SSH in your PATH you can set it up with the
91
+“set” command. For instance, if your ssh.exe is in the folder named
92
+“C:\Program Files (x86)\Git\bin”, then you can run the following
93
+command:
89 94
 
90 95
 .. code-block:: bash
91 96
 
... ...
@@ -104,13 +117,16 @@ First step is to get the IP and port of your Ubuntu server. Simply run:
104 104
 
105 105
 	vagrant ssh-config 
106 106
 
107
-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”.
107
+You should see an output with HostName and Port information. In this
108
+example, HostName is 127.0.0.1 and port is 2222. And the User is
109
+“vagrant”. The password is not shown, but it is also “vagrant”.
108 110
 
109 111
 .. image:: images/win/ssh-config.gif
110 112
    :alt: run docker
111 113
    :align: center
112 114
 
113
-You can now use this information for connecting via SSH to your server. To do so you can:
115
+You can now use this information for connecting via SSH to your
116
+server. To do so you can:
114 117
 
115 118
 - Use putty.exe OR
116 119
 - Use SSH from a terminal
... ...
@@ -118,8 +134,9 @@ You can now use this information for connecting via SSH to your server. To do so
118 118
 Use putty.exe
119 119
 '''''''''''''
120 120
 
121
-You can download putty.exe from this page http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
122
-Launch putty.exe and simply enter the information you got from last step.
121
+You can download putty.exe from this page
122
+http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html Launch
123
+putty.exe and simply enter the information you got from last step.
123 124
 
124 125
 .. image:: images/win/putty.gif
125 126
    :alt: run docker
... ...
@@ -134,7 +151,9 @@ Open, and enter user = vagrant and password = vagrant.
134 134
 SSH from a terminal
135 135
 '''''''''''''''''''
136 136
 
137
-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.
137
+You can also run this command on your favorite terminal (windows
138
+prompt, cygwin, git-bash, …). Make sure to adapt the IP and port from
139
+what you got from the vagrant ssh-config command.
138 140
 
139 141
 .. code-block:: bash
140 142
 
... ...
@@ -146,12 +165,14 @@ Enter user = vagrant and password = vagrant.
146 146
    :alt: run docker
147 147
    :align: center
148 148
 
149
-Congratulations, you are now logged onto your Ubuntu Server, running on top of your Windows machine !
149
+Congratulations, you are now logged onto your Ubuntu Server, running
150
+on top of your Windows machine !
150 151
 
151 152
 Running Docker
152 153
 --------------
153 154
 
154
-First you have to be root in order to run docker. Simply run the following command:
155
+First you have to be root in order to run docker. Simply run the
156
+following command:
155 157
 
156 158
 .. code-block:: bash
157 159
 
... ...
@@ -179,10 +200,11 @@ VM does not boot
179 179
 
180 180
 .. image:: images/win/ts_go_bios.JPG
181 181
 
182
-If you run into this error message "The VM failed to remain in the 'running'
183
-state while attempting to boot", please check that your computer has virtualization
184
-technology available and activated by going to the BIOS. Here's an example for an HP
185
-computer (System configuration / Device configuration)
182
+If you run into this error message "The VM failed to remain in the
183
+'running' state while attempting to boot", please check that your
184
+computer has virtualization technology available and activated by
185
+going to the BIOS. Here's an example for an HP computer (System
186
+configuration / Device configuration)
186 187
 
187 188
 .. image:: images/win/hp_bios_vm.JPG
188 189
 
... ...
@@ -192,5 +214,6 @@ Docker is not installed
192 192
 
193 193
 .. image:: images/win/ts_no_docker.JPG
194 194
 
195
-If you run into this error message "The program 'docker' is currently not installed",
196
-try deleting the docker folder and restart from :ref:`launch_ubuntu`
195
+If you run into this error message "The program 'docker' is currently
196
+not installed", try deleting the docker folder and restart from
197
+:ref:`launch_ubuntu`
... ...
@@ -37,6 +37,8 @@ Running an interactive shell
37 37
   # use the escape sequence Ctrl-p + Ctrl-q
38 38
   sudo docker run -i -t ubuntu /bin/bash
39 39
 
40
+.. _dockergroup:
41
+
40 42
 Why ``sudo``?
41 43
 -------------
42 44
 
... ...
@@ -140,7 +142,7 @@ Expose a service on a TCP port
140 140
 .. code-block:: bash
141 141
 
142 142
   # Expose port 4444 of this container, and tell netcat to listen on it
143
-  JOB=$(sudo docker run -d -p 4444 ubuntu /bin/nc -l -p 4444)
143
+  JOB=$(sudo docker run -d -p 4444 ubuntu:12.10 /bin/nc -l -p 4444)
144 144
 
145 145
   # Which public port is NATed to my container?
146 146
   PORT=$(sudo docker port $JOB 4444)
... ...
@@ -68,6 +68,10 @@ building images.
68 68
 
69 69
     ``FROM <image>``
70 70
 
71
+Or
72
+
73
+    ``FROM <image>:<tag>``
74
+
71 75
 The ``FROM`` instruction sets the :ref:`base_image_def` for subsequent
72 76
 instructions. As such, a valid Dockerfile must have ``FROM`` as its
73 77
 first instruction. The image can be any valid image -- it is
... ...
@@ -81,6 +85,9 @@ especially easy to start by **pulling an image** from the
81 81
 to create multiple images. Simply make a note of the last image id
82 82
 output by the commit before each new ``FROM`` command.
83 83
 
84
+If no ``tag`` is given to the ``FROM`` instruction, ``latest`` is
85
+assumed. If the used tag does not exist, an error will be returned.
86
+
84 87
 3.2 MAINTAINER
85 88
 --------------
86 89
 
... ...
@@ -8,7 +8,7 @@
8 8
 Port redirection
9 9
 ================
10 10
 
11
-Docker can redirect public TCP ports to your container, so it can be
11
+Docker can redirect public TCP and UDP ports to your container, so it can be
12 12
 reached over the network.  Port redirection is done on ``docker run``
13 13
 using the -p flag.
14 14
 
... ...
@@ -25,6 +25,12 @@ will be allocated.
25 25
     # PUBLIC port 80 is redirected to PRIVATE port 80
26 26
     sudo docker run -p 80:80 <image> <cmd>
27 27
 
28
+To redirect a UDP port the redirection must be expressed as *PUBLIC:PRIVATE/udp*:
29
+
30
+.. code-block:: bash
31
+
32
+    # PUBLIC port 5300 is redirected to the PRIVATE port 53 using UDP
33
+    sudo docker run -p 5300:53/udp <image> <cmd>
28 34
 
29 35
 Default port redirects can be built into a container with the
30 36
 ``EXPOSE`` build command.
... ...
@@ -110,6 +110,9 @@
110 110
 
111 111
         <div class="span3 sidebar bs-docs-sidebar">
112 112
             {{ toctree(collapse=False, maxdepth=3) }}
113
+	    <form>
114
+	      <input type="text" id="st-search-input" class="st-search-input span3" style="width:160px;" />
115
+	    </form>
113 116
         </div>
114 117
 
115 118
         <!-- body block -->
... ...
@@ -121,6 +124,26 @@
121 121
                 {% block body %}{% endblock %}
122 122
             </section>
123 123
 
124
+	    <!-- Swiftype search -->
125
+	    <div id="st-results-container"></div>
126
+	    <script type="text/javascript">
127
+	      var Swiftype = window.Swiftype || {};
128
+	      (function() {
129
+	      Swiftype.key = 'pWPnnyvwcfpcrw1o51Sz';
130
+	      Swiftype.inputElement = '#st-search-input';
131
+	      Swiftype.resultContainingElement = '#st-results-container';
132
+	      Swiftype.attachElement = '#st-search-input';
133
+	      Swiftype.renderStyle = "overlay";
134
+
135
+	      var script = document.createElement('script');
136
+	      script.type = 'text/javascript';
137
+	      script.async = true;
138
+	      script.src = "//swiftype.com/embed.js";
139
+	      var entry = document.getElementsByTagName('script')[0];
140
+	      entry.parentNode.insertBefore(script, entry);
141
+	      }());
142
+	    </script>
143
+
124 144
         </div>
125 145
     </div>
126 146
 </div>
... ...
@@ -391,3 +391,21 @@ dt:hover > a.headerlink {
391 391
   float: right;
392 392
   visibility: hidden;
393 393
 }
394
+
395
+/* Swiftype style */
396
+
397
+#st-search-input {
398
+  margin-right: 14px;
399
+  margin-left: 9px;
400
+  height: 19px;
401
+  width: 120px;
402
+
403
+}
404
+#swiftype-img {
405
+    border: none;
406
+    width: 145px;
407
+    height: auto;
408
+    margin: 0px auto;
409
+    margin-left: 13px;
410
+    margin-top: -30px;
411
+}
394 412
\ No newline at end of file
... ...
@@ -202,6 +202,8 @@ func (graph *Graph) getDockerInitLayer() (string, error) {
202 202
 		"/sys":             "dir",
203 203
 		"/.dockerinit":     "file",
204 204
 		"/etc/resolv.conf": "file",
205
+		"/etc/hosts":       "file",
206
+		"/etc/hostname":    "file",
205 207
 		// "var/run": "dir",
206 208
 		// "var/lock": "dir",
207 209
 	} {
... ...
@@ -272,30 +274,19 @@ func (graph *Graph) Delete(name string) error {
272 272
 
273 273
 // Map returns a list of all images in the graph, addressable by ID.
274 274
 func (graph *Graph) Map() (map[string]*Image, error) {
275
-	// FIXME: this should replace All()
276
-	all, err := graph.All()
275
+	images := make(map[string]*Image)
276
+	err := graph.walkAll(func(image *Image) {
277
+		images[image.ID] = image
278
+	})
277 279
 	if err != nil {
278 280
 		return nil, err
279 281
 	}
280
-	images := make(map[string]*Image, len(all))
281
-	for _, image := range all {
282
-		images[image.ID] = image
283
-	}
284 282
 	return images, nil
285 283
 }
286 284
 
287
-// All returns a list of all images in the graph.
288
-func (graph *Graph) All() ([]*Image, error) {
289
-	var images []*Image
290
-	err := graph.WalkAll(func(image *Image) {
291
-		images = append(images, image)
292
-	})
293
-	return images, err
294
-}
295
-
296
-// WalkAll iterates over each image in the graph, and passes it to a handler.
285
+// walkAll iterates over each image in the graph, and passes it to a handler.
297 286
 // The walking order is undetermined.
298
-func (graph *Graph) WalkAll(handler func(*Image)) error {
287
+func (graph *Graph) walkAll(handler func(*Image)) error {
299 288
 	files, err := ioutil.ReadDir(graph.Root)
300 289
 	if err != nil {
301 290
 		return err
... ...
@@ -317,7 +308,7 @@ func (graph *Graph) WalkAll(handler func(*Image)) error {
317 317
 // If an image has no children, it will not have an entry in the table.
318 318
 func (graph *Graph) ByParent() (map[string][]*Image, error) {
319 319
 	byParent := make(map[string][]*Image)
320
-	err := graph.WalkAll(func(image *Image) {
320
+	err := graph.walkAll(func(image *Image) {
321 321
 		parent, err := graph.Get(image.Parent)
322 322
 		if err != nil {
323 323
 			return
... ...
@@ -339,7 +330,7 @@ func (graph *Graph) Heads() (map[string]*Image, error) {
339 339
 	if err != nil {
340 340
 		return nil, err
341 341
 	}
342
-	err = graph.WalkAll(func(image *Image) {
342
+	err = graph.walkAll(func(image *Image) {
343 343
 		// If it's not in the byParent lookup table, then
344 344
 		// it's not a parent -> so it's a head!
345 345
 		if _, exists := byParent[image.ID]; !exists {
... ...
@@ -20,11 +20,11 @@ func TestInit(t *testing.T) {
20 20
 	if _, err := os.Stat(graph.Root); err != nil {
21 21
 		t.Fatal(err)
22 22
 	}
23
-	// All() should be empty
24
-	if l, err := graph.All(); err != nil {
23
+	// Map() should be empty
24
+	if l, err := graph.Map(); err != nil {
25 25
 		t.Fatal(err)
26 26
 	} else if len(l) != 0 {
27
-		t.Fatalf("List() should return %d, not %d", 0, len(l))
27
+		t.Fatalf("len(Map()) should return %d, not %d", 0, len(l))
28 28
 	}
29 29
 }
30 30
 
... ...
@@ -76,11 +76,15 @@ func TestGraphCreate(t *testing.T) {
76 76
 	if image.DockerVersion != VERSION {
77 77
 		t.Fatalf("Wrong docker_version: should be '%s', not '%s'", VERSION, image.DockerVersion)
78 78
 	}
79
-	if images, err := graph.All(); err != nil {
79
+	images, err := graph.Map()
80
+	if err != nil {
80 81
 		t.Fatal(err)
81 82
 	} else if l := len(images); l != 1 {
82 83
 		t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
83 84
 	}
85
+	if images[image.ID] == nil {
86
+		t.Fatalf("Could not find image with id %s", image.ID)
87
+	}
84 88
 }
85 89
 
86 90
 func TestRegister(t *testing.T) {
... ...
@@ -99,7 +103,7 @@ func TestRegister(t *testing.T) {
99 99
 	if err != nil {
100 100
 		t.Fatal(err)
101 101
 	}
102
-	if images, err := graph.All(); err != nil {
102
+	if images, err := graph.Map(); err != nil {
103 103
 		t.Fatal(err)
104 104
 	} else if l := len(images); l != 1 {
105 105
 		t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
... ...
@@ -274,7 +278,7 @@ func TestByParent(t *testing.T) {
274 274
 }
275 275
 
276 276
 func assertNImages(graph *Graph, t *testing.T, n int) {
277
-	if images, err := graph.All(); err != nil {
277
+	if images, err := graph.Map(); err != nil {
278 278
 		t.Fatal(err)
279 279
 	} else if actualN := len(images); actualN != n {
280 280
 		t.Fatalf("Expected %d images, found %d", n, actualN)
... ...
@@ -7,99 +7,39 @@ For a more complete view of planned and requested improvements, see [the Github
7 7
 
8 8
 Tu suggest changes to the roadmap, including additions, please write the change as if it were already in effect, and make a pull request.
9 9
 
10
-Broader kernel support
11 10
 
12
-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.
13
-However, if you're adding Docker to an existing deployment, you may not have the flexibility to update and patch the kernel.
11
+## Container wiring and service discovery
14 12
 
15
-Expanding Docker's kernel support is a priority. This includes running on older kernel versions,
16
-but also on kernels with no AUFS support, or with incomplete lxc capabilities.
13
+In its current version, docker doesn’t make it very easy to manipulate multiple containers as a cohesive group (ie. orchestration), and it doesn’t make it seamless for containers to connect to each other as network services (ie. wiring).
17 14
 
15
+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.
18 16
 
19
-Cross-architecture support
17
+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.
20 18
 
21
-Our goal is to make Docker run everywhere. However currently Docker only runs on x86_64 systems.
22
-We plan on expanding architecture support, so that Docker containers can be created and used on more architectures.
23 19
 
20
+## Better integration with process supervisors
24 21
 
25
-Even more integrations
22
+For docker to be fully usable in production, it needs to cleanly integrate with the host machine’s process supervisor of choice. Whether it’s 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.
26 23
 
27
-We want Docker to be the secret ingredient that makes your existing tools more awesome.
28
-Thanks to this philosophy, Docker has already been integrated with
29
-[Puppet](http://forge.puppetlabs.com/garethr/docker),  [Chef](http://www.opscode.com/chef),
30
-[Openstack Nova](https://github.com/dotcloud/openstack-docker), [Jenkins](https://github.com/georgebashi/jenkins-docker-plugin),
31
-[DotCloud sandbox](http://github.com/dotcloud/sandbox), [Pallet](https://github.com/pallet/pallet-docker),
32
-[Strider CI](http://blog.frozenridge.co/next-generation-continuous-integration-deployment-with-dotclouds-docker-and-strider/)
33
-and even [Heroku buildpacks](https://github.com/progrium/buildstep).
34 24
 
35
-Expect Docker to integrate with even more of your favorite tools going forward, including:
25
+## Plugin API
36 26
 
37
-* Alternative storage backends such as ZFS, LVM or [BTRFS](github.com/dotcloud/docker/issues/443)
38
-* Alternative containerization backends such as [OpenVZ](http://openvz.org), Solaris Zones, BSD Jails and even plain Chroot.
39
-* 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/)
40
-* Build and integration tools like Make, Maven, Scons, Jenkins, Buildbot and Cruise Control.
41
-* Configuration management tools like [Puppet](http://puppetlabs.com), [Chef](http://www.opscode.com/chef/) and [Salt](http://saltstack.org)
42
-* Personal development environments like [Vagrant](http://vagrantup.com), [Boxen](http://boxen.github.com/), [Koding](http://koding.com) and [Cloud9](http://c9.io).
43
-* Orchestration tools like [Zookeeper](http://zookeeper.apache.org/), [Mesos](http://incubator.apache.org/mesos/) and [Galaxy](https://github.com/ning/galaxy)
44
-* Infrastructure deployment tools like [Openstack](http://openstack.org), [Apache Cloudstack](http://apache.cloudstack.org), [Ganeti](https://code.google.com/p/ganeti/)
27
+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.
45 28
 
29
+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 didn’t even think about.
46 30
 
47
-Plugin API
48 31
 
49
-We want Docker to run everywhere, and to integrate with every devops tool.
50
-Those are ambitious goals, and the only way to reach them is with the Docker community.
51
-For the community to participate fully, we need an API which allows Docker to be deeply and easily customized.
32
+## Broader kernel support
52 33
 
53
-We are working on a plugin API which will make Docker very, very customization-friendly.
54
-We believe it will facilitate the integrations listed above - and many more we didn't even think about.
34
+Our goal is to make Docker run everywhere, but currently Docker requires Linux version 3.8 or higher with lxc and aufs support. If you’re deploying new machines for the purpose of running Docker, this is a fairly easy requirement to meet. However, if you’re adding Docker to an existing deployment, you may not have the flexibility to update and patch the kernel.
55 35
 
56
-Let us know if you want to start playing with the API before it's generally available.
36
+Expanding Docker’s 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.
57 37
 
58 38
 
59
-Externally mounted volumes
39
+## Cross-architecture support
60 40
 
61
-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),
62
-a great mechanism for manipulating persistent data such as database files, log files, etc.
41
+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.
63 42
 
64
-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,
65
-making live development changes available to a container, etc.
43
+## Production-ready
66 44
 
67
-
68
-Better documentation
69
-
70
-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".
71
-Our goal is to make it great, period.
72
-
73
-If you have feedback on how to improve our documentation, please get in touch by replying to this email,
74
-or by [filing an issue](https://github.com/dotcloud/docker/issues). We always appreciate it!
75
-
76
-
77
-Production-ready
78
-
79
-Docker is still alpha software, and not suited for production.
80
-We are working hard to get there, and we are confident that it will be possible within a few months.
81
-
82
-
83
-Advanced port redirections
84
-
85
-Docker currently supports 2 flavors of port redirection: STATIC->STATIC (eg. "redirect public port 80 to private port 80")
86
-and RANDOM->STATIC (eg. "redirect any public port to private port 80").
87
-
88
-With these 2 flavors, docker can support the majority of backend programs out there. But some applications have more exotic
89
-requirements, generally to implement custom clustering techniques. These applications include Hadoop, MongoDB, Riak, RabbitMQ,
90
-Disco, and all programs relying on Erlang's OTP.
91
-
92
-To support these applications, Docker needs to support more advanced redirection flavors, including:
93
-
94
-* RANDOM->RANDOM
95
-* STATIC1->STATIC2
96
-
97
-These flavors should be implemented without breaking existing semantics, if at all possible.
45
+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.
98 46
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+docker-ci github pull request
1
+=============================
2
+
3
+The entire docker pull request test workflow is event driven by github. Its
4
+usage is fully automatic and the results are logged in docker-ci.dotcloud.com
5
+
6
+Each time there is a pull request on docker's github project, github connects
7
+to docker-ci using github's rest API documented in http://developer.github.com/v3/repos/hooks
8
+The issued command to program github's notification PR event was:
9
+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
10
+
11
+buildbot (0.8.7p1) was patched using ./testing/buildbot/github.py, so it
12
+can understand the PR data github sends to it. Originally PR #1603 (ee64e099e0)
13
+implemented this capability. Also we added a new scheduler to exclusively filter
14
+PRs. and the 'pullrequest' builder to rebase the PR on top of master and test it.
... ...
@@ -52,7 +52,7 @@ private PaaS, service-oriented architectures, etc."
52 52
 
53 53
 UPSTART_SCRIPT='description     "Docker daemon"
54 54
 
55
-start on filesystem or runlevel [2345]
55
+start on filesystem and started lxc-net
56 56
 stop on runlevel [!2345]
57 57
 
58 58
 respawn
... ...
@@ -30,6 +30,10 @@ lxc.network.ipv4 = {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen
30 30
 {{$ROOTFS := .RootfsPath}}
31 31
 lxc.rootfs = {{$ROOTFS}}
32 32
 
33
+# enable domain name support
34
+lxc.mount.entry = {{.HostnamePath}} {{$ROOTFS}}/etc/hostname none bind,ro 0 0
35
+lxc.mount.entry = {{.HostsPath}} {{$ROOTFS}}/etc/hosts none bind,ro 0 0
36
+
33 37
 # use a dedicated pts for the container (and limit the number of pseudo terminal
34 38
 # available)
35 39
 lxc.pts = 1024
... ...
@@ -642,7 +642,7 @@ func (manager *NetworkManager) Allocate() (*NetworkInterface, error) {
642 642
 	if err != nil {
643 643
 		return nil, err
644 644
 	}
645
-	// avoid duplicate IP 
645
+	// avoid duplicate IP
646 646
 	ipNum := ipToInt(ip)
647 647
 	firstIP := manager.ipAllocator.network.IP.To4().Mask(manager.ipAllocator.network.Mask)
648 648
 	firstIPNum := ipToInt(firstIP) + 1
... ...
@@ -1,6 +1,6 @@
1 1
 description     "Run docker"
2 2
 
3
-start on filesystem or runlevel [2345]
3
+start on filesystem and started lxc-net
4 4
 stop on runlevel [!2345]
5 5
 
6 6
 respawn
... ...
@@ -22,6 +22,7 @@ import (
22 22
 var (
23 23
 	ErrAlreadyExists         = errors.New("Image already exists")
24 24
 	ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
25
+	ErrLoginRequired         = errors.New("Authentication is required.")
25 26
 )
26 27
 
27 28
 func pingRegistryEndpoint(endpoint string) error {
... ...
@@ -102,17 +103,38 @@ func ResolveRepositoryName(reposName string) (string, string, error) {
102 102
 	if err := validateRepositoryName(reposName); err != nil {
103 103
 		return "", "", err
104 104
 	}
105
+	endpoint, err := ExpandAndVerifyRegistryUrl(hostname)
106
+	if err != nil {
107
+		return "", "", err
108
+	}
109
+	return endpoint, reposName, err
110
+}
111
+
112
+// this method expands the registry name as used in the prefix of a repo
113
+// to a full url. if it already is a url, there will be no change.
114
+// The registry is pinged to test if it http or https
115
+func ExpandAndVerifyRegistryUrl(hostname string) (string, error) {
116
+	if strings.HasPrefix(hostname, "http:") || strings.HasPrefix(hostname, "https:") {
117
+		// if there is no slash after https:// (8 characters) then we have no path in the url
118
+		if strings.LastIndex(hostname, "/") < 9 {
119
+			// there is no path given. Expand with default path
120
+			hostname = hostname + "/v1/"
121
+		}
122
+		if err := pingRegistryEndpoint(hostname); err != nil {
123
+			return "", errors.New("Invalid Registry endpoint: " + err.Error())
124
+		}
125
+		return hostname, nil
126
+	}
105 127
 	endpoint := fmt.Sprintf("https://%s/v1/", hostname)
106 128
 	if err := pingRegistryEndpoint(endpoint); err != nil {
107 129
 		utils.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err)
108 130
 		endpoint = fmt.Sprintf("http://%s/v1/", hostname)
109 131
 		if err = pingRegistryEndpoint(endpoint); err != nil {
110 132
 			//TODO: triggering highland build can be done there without "failing"
111
-			return "", "", errors.New("Invalid Registry endpoint: " + err.Error())
133
+			return "", errors.New("Invalid Registry endpoint: " + err.Error())
112 134
 		}
113 135
 	}
114
-	err := validateRepositoryName(reposName)
115
-	return endpoint, reposName, err
136
+	return endpoint, nil
116 137
 }
117 138
 
118 139
 func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) {
... ...
@@ -139,6 +161,9 @@ func (r *Registry) GetRemoteHistory(imgID, registry string, token []string) ([]s
139 139
 	req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
140 140
 	res, err := doWithCookies(r.client, req)
141 141
 	if err != nil || res.StatusCode != 200 {
142
+		if res.StatusCode == 401 {
143
+			return nil, ErrLoginRequired
144
+		}
142 145
 		if res != nil {
143 146
 			return nil, utils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to fetch remote history for %s", res.StatusCode, imgID), res)
144 147
 		}
... ...
@@ -282,7 +307,7 @@ func (r *Registry) GetRepositoryData(indexEp, remote string) (*RepositoryData, e
282 282
 	}
283 283
 	defer res.Body.Close()
284 284
 	if res.StatusCode == 401 {
285
-		return nil, utils.NewHTTPRequestError(fmt.Sprintf("Please login first (HTTP code %d)", res.StatusCode), res)
285
+		return nil, ErrLoginRequired
286 286
 	}
287 287
 	// TODO: Right now we're ignoring checksums in the response body.
288 288
 	// In the future, we need to use them to check image validity.
... ...
@@ -12,8 +12,11 @@ import (
12 12
 	"path"
13 13
 	"sort"
14 14
 	"strings"
15
+	"time"
15 16
 )
16 17
 
18
+var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
19
+
17 20
 type Capabilities struct {
18 21
 	MemoryLimit            bool
19 22
 	SwapLimit              bool
... ...
@@ -42,6 +45,7 @@ func init() {
42 42
 	sysInitPath = utils.SelfPath()
43 43
 }
44 44
 
45
+// List returns an array of all containers registered in the runtime.
45 46
 func (runtime *Runtime) List() []*Container {
46 47
 	containers := new(History)
47 48
 	for e := runtime.containers.Front(); e != nil; e = e.Next() {
... ...
@@ -60,6 +64,8 @@ func (runtime *Runtime) getContainerElement(id string) *list.Element {
60 60
 	return nil
61 61
 }
62 62
 
63
+// Get looks for a container by the specified ID or name, and returns it.
64
+// If the container is not found, or if an error occurs, nil is returned.
63 65
 func (runtime *Runtime) Get(name string) *Container {
64 66
 	id, err := runtime.idIndex.Get(name)
65 67
 	if err != nil {
... ...
@@ -72,6 +78,8 @@ func (runtime *Runtime) Get(name string) *Container {
72 72
 	return e.Value.(*Container)
73 73
 }
74 74
 
75
+// Exists returns a true if a container of the specified ID or name exists,
76
+// false otherwise.
75 77
 func (runtime *Runtime) Exists(id string) bool {
76 78
 	return runtime.Get(id) != nil
77 79
 }
... ...
@@ -80,6 +88,9 @@ func (runtime *Runtime) containerRoot(id string) string {
80 80
 	return path.Join(runtime.repository, id)
81 81
 }
82 82
 
83
+// Load reads the contents of a container from disk and registers
84
+// it with Register.
85
+// This is typically done at startup.
83 86
 func (runtime *Runtime) Load(id string) (*Container, error) {
84 87
 	container := &Container{root: runtime.containerRoot(id)}
85 88
 	if err := container.FromDisk(); err != nil {
... ...
@@ -177,6 +188,7 @@ func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream strin
177 177
 	return nil
178 178
 }
179 179
 
180
+// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem.
180 181
 func (runtime *Runtime) Destroy(container *Container) error {
181 182
 	if container == nil {
182 183
 		return fmt.Errorf("The given container is <nil>")
... ...
@@ -208,7 +220,7 @@ func (runtime *Runtime) Destroy(container *Container) error {
208 208
 
209 209
 func (runtime *Runtime) restore() error {
210 210
 	wheel := "-\\|/"
211
-	if os.Getenv("DEBUG") == "" {
211
+	if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
212 212
 		fmt.Printf("Loading containers:  ")
213 213
 	}
214 214
 	dir, err := ioutil.ReadDir(runtime.repository)
... ...
@@ -218,7 +230,7 @@ func (runtime *Runtime) restore() error {
218 218
 	for i, v := range dir {
219 219
 		id := v.Name()
220 220
 		container, err := runtime.Load(id)
221
-		if i%21 == 0 && os.Getenv("DEBUG") == "" {
221
+		if i%21 == 0 && os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
222 222
 			fmt.Printf("\b%c", wheel[i%4])
223 223
 		}
224 224
 		if err != nil {
... ...
@@ -227,12 +239,13 @@ func (runtime *Runtime) restore() error {
227 227
 		}
228 228
 		utils.Debugf("Loaded container %v", container.ID)
229 229
 	}
230
-	if os.Getenv("DEBUG") == "" {
230
+	if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
231 231
 		fmt.Printf("\bdone.\n")
232 232
 	}
233 233
 	return nil
234 234
 }
235 235
 
236
+// FIXME: comment please!
236 237
 func (runtime *Runtime) UpdateCapabilities(quiet bool) {
237 238
 	if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil {
238 239
 		if !quiet {
... ...
@@ -260,6 +273,159 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) {
260 260
 	}
261 261
 }
262 262
 
263
+// Create creates a new container from the given configuration.
264
+func (runtime *Runtime) Create(config *Config) (*Container, error) {
265
+	// Lookup image
266
+	img, err := runtime.repositories.LookupImage(config.Image)
267
+	if err != nil {
268
+		return nil, err
269
+	}
270
+
271
+	if img.Config != nil {
272
+		MergeConfig(config, img.Config)
273
+	}
274
+
275
+	if len(config.Entrypoint) != 0 && config.Cmd == nil {
276
+		config.Cmd = []string{}
277
+	} else if config.Cmd == nil || len(config.Cmd) == 0 {
278
+		return nil, fmt.Errorf("No command specified")
279
+	}
280
+
281
+	// Generate id
282
+	id := GenerateID()
283
+	// Generate default hostname
284
+	// FIXME: the lxc template no longer needs to set a default hostname
285
+	if config.Hostname == "" {
286
+		config.Hostname = id[:12]
287
+	}
288
+
289
+	var args []string
290
+	var entrypoint string
291
+
292
+	if len(config.Entrypoint) != 0 {
293
+		entrypoint = config.Entrypoint[0]
294
+		args = append(config.Entrypoint[1:], config.Cmd...)
295
+	} else {
296
+		entrypoint = config.Cmd[0]
297
+		args = config.Cmd[1:]
298
+	}
299
+
300
+	container := &Container{
301
+		// FIXME: we should generate the ID here instead of receiving it as an argument
302
+		ID:              id,
303
+		Created:         time.Now(),
304
+		Path:            entrypoint,
305
+		Args:            args, //FIXME: de-duplicate from config
306
+		Config:          config,
307
+		Image:           img.ID, // Always use the resolved image id
308
+		NetworkSettings: &NetworkSettings{},
309
+		// FIXME: do we need to store this in the container?
310
+		SysInitPath: sysInitPath,
311
+	}
312
+	container.root = runtime.containerRoot(container.ID)
313
+	// Step 1: create the container directory.
314
+	// This doubles as a barrier to avoid race conditions.
315
+	if err := os.Mkdir(container.root, 0700); err != nil {
316
+		return nil, err
317
+	}
318
+
319
+	resolvConf, err := utils.GetResolvConf()
320
+	if err != nil {
321
+		return nil, err
322
+	}
323
+
324
+	if len(config.Dns) == 0 && len(runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
325
+		//"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns
326
+		runtime.Dns = defaultDns
327
+	}
328
+
329
+	// If custom dns exists, then create a resolv.conf for the container
330
+	if len(config.Dns) > 0 || len(runtime.Dns) > 0 {
331
+		var dns []string
332
+		if len(config.Dns) > 0 {
333
+			dns = config.Dns
334
+		} else {
335
+			dns = runtime.Dns
336
+		}
337
+		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
338
+		f, err := os.Create(container.ResolvConfPath)
339
+		if err != nil {
340
+			return nil, err
341
+		}
342
+		defer f.Close()
343
+		for _, dns := range dns {
344
+			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
345
+				return nil, err
346
+			}
347
+		}
348
+	} else {
349
+		container.ResolvConfPath = "/etc/resolv.conf"
350
+	}
351
+
352
+	// Step 2: save the container json
353
+	if err := container.ToDisk(); err != nil {
354
+		return nil, err
355
+	}
356
+
357
+	// Step 3: if hostname, build hostname and hosts files
358
+	container.HostnamePath = path.Join(container.root, "hostname")
359
+	ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
360
+
361
+	hostsContent := []byte(`
362
+127.0.0.1	localhost
363
+::1		localhost ip6-localhost ip6-loopback
364
+fe00::0		ip6-localnet
365
+ff00::0		ip6-mcastprefix
366
+ff02::1		ip6-allnodes
367
+ff02::2		ip6-allrouters
368
+`)
369
+
370
+	container.HostsPath = path.Join(container.root, "hosts")
371
+
372
+	if container.Config.Domainname != "" {
373
+		hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
374
+		hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
375
+	} else {
376
+		hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s\n", container.Config.Hostname)), hostsContent...)
377
+		hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s\n", container.Config.Hostname)), hostsContent...)
378
+	}
379
+
380
+	ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
381
+
382
+	// Step 4: register the container
383
+	if err := runtime.Register(container); err != nil {
384
+		return nil, err
385
+	}
386
+	return container, nil
387
+}
388
+
389
+// Commit creates a new filesystem image from the current state of a container.
390
+// The image can optionally be tagged into a repository
391
+func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
392
+	// FIXME: freeze the container before copying it to avoid data corruption?
393
+	// FIXME: this shouldn't be in commands.
394
+	if err := container.EnsureMounted(); err != nil {
395
+		return nil, err
396
+	}
397
+
398
+	rwTar, err := container.ExportRw()
399
+	if err != nil {
400
+		return nil, err
401
+	}
402
+	// Create a new image from the container's base layers + a new layer from container changes
403
+	img, err := runtime.graph.Create(rwTar, container, comment, author, config)
404
+	if err != nil {
405
+		return nil, err
406
+	}
407
+	// Register the image if needed
408
+	if repository != "" {
409
+		if err := runtime.repositories.Set(repository, tag, img.ID, true); err != nil {
410
+			return img, err
411
+		}
412
+	}
413
+	return img, nil
414
+}
415
+
263 416
 // FIXME: harmonize with NewGraph()
264 417
 func NewRuntime(flGraphPath string, autoRestart bool, dns []string) (*Runtime, error) {
265 418
 	runtime, err := NewRuntimeFromDirectory(flGraphPath, autoRestart)
... ...
@@ -325,6 +491,8 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
325 325
 	return runtime, nil
326 326
 }
327 327
 
328
+// History is a convenience type for storing a list of containers,
329
+// ordered by creation date.
328 330
 type History []*Container
329 331
 
330 332
 func (history *History) Len() int {
... ...
@@ -50,7 +50,7 @@ func cleanup(runtime *Runtime) error {
50 50
 		container.Kill()
51 51
 		runtime.Destroy(container)
52 52
 	}
53
-	images, err := runtime.graph.All()
53
+	images, err := runtime.graph.Map()
54 54
 	if err != nil {
55 55
 		return err
56 56
 	}
... ...
@@ -72,6 +72,8 @@ func layerArchive(tarfile string) (io.Reader, error) {
72 72
 }
73 73
 
74 74
 func init() {
75
+	os.Setenv("TEST", "1")
76
+
75 77
 	// Hack to run sys init during unit testing
76 78
 	if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" {
77 79
 		SysInit()
... ...
@@ -121,13 +123,13 @@ func init() {
121 121
 // FIXME: test that ImagePull(json=true) send correct json output
122 122
 
123 123
 func GetTestImage(runtime *Runtime) *Image {
124
-	imgs, err := runtime.graph.All()
124
+	imgs, err := runtime.graph.Map()
125 125
 	if err != nil {
126 126
 		panic(err)
127 127
 	}
128
-	for i := range imgs {
129
-		if imgs[i].ID == unitTestImageID {
130
-			return imgs[i]
128
+	for _, image := range imgs {
129
+		if image.ID == unitTestImageID {
130
+			return image
131 131
 		}
132 132
 	}
133 133
 	panic(fmt.Errorf("Test image %v not found", unitTestImageID))
... ...
@@ -142,9 +144,7 @@ func TestRuntimeCreate(t *testing.T) {
142 142
 		t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
143 143
 	}
144 144
 
145
-	builder := NewBuilder(runtime)
146
-
147
-	container, err := builder.Create(&Config{
145
+	container, err := runtime.Create(&Config{
148 146
 		Image: GetTestImage(runtime).ID,
149 147
 		Cmd:   []string{"ls", "-al"},
150 148
 	},
... ...
@@ -185,7 +185,7 @@ func TestRuntimeCreate(t *testing.T) {
185 185
 	}
186 186
 
187 187
 	// Make sure crete with bad parameters returns an error
188
-	_, err = builder.Create(
188
+	_, err = runtime.Create(
189 189
 		&Config{
190 190
 			Image: GetTestImage(runtime).ID,
191 191
 		},
... ...
@@ -194,7 +194,7 @@ func TestRuntimeCreate(t *testing.T) {
194 194
 		t.Fatal("Builder.Create should throw an error when Cmd is missing")
195 195
 	}
196 196
 
197
-	_, err = builder.Create(
197
+	_, err = runtime.Create(
198 198
 		&Config{
199 199
 			Image: GetTestImage(runtime).ID,
200 200
 			Cmd:   []string{},
... ...
@@ -208,7 +208,7 @@ func TestRuntimeCreate(t *testing.T) {
208 208
 func TestDestroy(t *testing.T) {
209 209
 	runtime := mkRuntime(t)
210 210
 	defer nuke(runtime)
211
-	container, err := NewBuilder(runtime).Create(&Config{
211
+	container, err := runtime.Create(&Config{
212 212
 		Image: GetTestImage(runtime).ID,
213 213
 		Cmd:   []string{"ls", "-al"},
214 214
 	},
... ...
@@ -294,7 +294,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container,
294 294
 			t.Fatal(fmt.Errorf("Unknown protocol %v", proto))
295 295
 		}
296 296
 		t.Log("Trying port", strPort)
297
-		container, err = NewBuilder(runtime).Create(&Config{
297
+		container, err = runtime.Create(&Config{
298 298
 			Image:     GetTestImage(runtime).ID,
299 299
 			Cmd:       []string{"sh", "-c", cmd},
300 300
 			PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)},
... ...
@@ -139,8 +139,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
139 139
 		return "", err
140 140
 	}
141 141
 
142
-	b := NewBuilder(srv.runtime)
143
-	c, err := b.Create(config)
142
+	c, err := srv.runtime.Create(config)
144 143
 	if err != nil {
145 144
 		return "", err
146 145
 	}
... ...
@@ -149,7 +148,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
149 149
 		return "", err
150 150
 	}
151 151
 	// FIXME: Handle custom repo, tag comment, author
152
-	img, err = b.Commit(c, "", "", img.Comment, img.Author, nil)
152
+	img, err = srv.runtime.Commit(c, "", "", img.Comment, img.Author, nil)
153 153
 	if err != nil {
154 154
 		return "", err
155 155
 	}
... ...
@@ -158,7 +157,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
158 158
 }
159 159
 
160 160
 func (srv *Server) ImagesViz(out io.Writer) error {
161
-	images, _ := srv.runtime.graph.All()
161
+	images, _ := srv.runtime.graph.Map()
162 162
 	if images == nil {
163 163
 		return nil
164 164
 	}
... ...
@@ -210,8 +209,10 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
210 210
 	}
211 211
 	outs := []APIImages{} //produce [] when empty instead of 'null'
212 212
 	for name, repository := range srv.runtime.repositories.Repositories {
213
-		if filter != "" && name != filter {
214
-			continue
213
+		if filter != "" {
214
+			if match, _ := path.Match(filter, name); !match {
215
+				continue
216
+			}
215 217
 		}
216 218
 		for tag, id := range repository {
217 219
 			var out APIImages
... ...
@@ -247,7 +248,7 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
247 247
 }
248 248
 
249 249
 func (srv *Server) DockerInfo() *APIInfo {
250
-	images, _ := srv.runtime.graph.All()
250
+	images, _ := srv.runtime.graph.Map()
251 251
 	var imgcount int
252 252
 	if images == nil {
253 253
 		imgcount = 0
... ...
@@ -386,7 +387,7 @@ func (srv *Server) Containers(all, size bool, n int, since, before string) []API
386 386
 		c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
387 387
 		c.Created = container.Created.Unix()
388 388
 		c.Status = container.State.String()
389
-		c.Ports = container.NetworkSettings.PortMappingHuman()
389
+		c.Ports = container.NetworkSettings.PortMappingAPI()
390 390
 		if size {
391 391
 			c.SizeRw, c.SizeRootFs = container.GetSize()
392 392
 		}
... ...
@@ -400,7 +401,7 @@ func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, conf
400 400
 	if container == nil {
401 401
 		return "", fmt.Errorf("No such container: %s", name)
402 402
 	}
403
-	img, err := NewBuilder(srv.runtime).Commit(container, repo, tag, comment, author, config)
403
+	img, err := srv.runtime.Commit(container, repo, tag, comment, author, config)
404 404
 	if err != nil {
405 405
 		return "", err
406 406
 	}
... ...
@@ -655,6 +656,9 @@ func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *ut
655 655
 
656 656
 	out = utils.NewWriteFlusher(out)
657 657
 	err = srv.pullRepository(r, out, localName, remoteName, tag, endpoint, sf, parallel)
658
+	if err == registry.ErrLoginRequired {
659
+		return err
660
+	}
658 661
 	if err != nil {
659 662
 		if err := srv.pullImage(r, out, remoteName, endpoint, nil, sf); err != nil {
660 663
 			return err
... ...
@@ -667,29 +671,57 @@ func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *ut
667 667
 
668 668
 // Retrieve the all the images to be uploaded in the correct order
669 669
 // Note: we can't use a map as it is not ordered
670
-func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgData, error) {
671
-	var imgList []*registry.ImgData
670
+func (srv *Server) getImageList(localRepo map[string]string) ([][]*registry.ImgData, error) {
671
+	imgList := map[string]*registry.ImgData{}
672
+	depGraph := utils.NewDependencyGraph()
672 673
 
673
-	imageSet := make(map[string]struct{})
674 674
 	for tag, id := range localRepo {
675 675
 		img, err := srv.runtime.graph.Get(id)
676 676
 		if err != nil {
677 677
 			return nil, err
678 678
 		}
679
-		img.WalkHistory(func(img *Image) error {
680
-			if _, exists := imageSet[img.ID]; exists {
679
+		depGraph.NewNode(img.ID)
680
+		img.WalkHistory(func(current *Image) error {
681
+			imgList[current.ID] = &registry.ImgData{
682
+				ID:  current.ID,
683
+				Tag: tag,
684
+			}
685
+			parent, err := current.GetParent()
686
+			if err != nil {
687
+				return err
688
+			}
689
+			if parent == nil {
681 690
 				return nil
682 691
 			}
683
-			imageSet[img.ID] = struct{}{}
684
-
685
-			imgList = append([]*registry.ImgData{{
686
-				ID:  img.ID,
687
-				Tag: tag,
688
-			}}, imgList...)
692
+			depGraph.NewNode(parent.ID)
693
+			depGraph.AddDependency(current.ID, parent.ID)
689 694
 			return nil
690 695
 		})
691 696
 	}
692
-	return imgList, nil
697
+
698
+	traversalMap, err := depGraph.GenerateTraversalMap()
699
+	if err != nil {
700
+		return nil, err
701
+	}
702
+
703
+	utils.Debugf("Traversal map: %v", traversalMap)
704
+	result := [][]*registry.ImgData{}
705
+	for _, round := range traversalMap {
706
+		dataRound := []*registry.ImgData{}
707
+		for _, imgID := range round {
708
+			dataRound = append(dataRound, imgList[imgID])
709
+		}
710
+		result = append(result, dataRound)
711
+	}
712
+	return result, nil
713
+}
714
+
715
+func flatten(slc [][]*registry.ImgData) []*registry.ImgData {
716
+	result := []*registry.ImgData{}
717
+	for _, x := range slc {
718
+		result = append(result, x...)
719
+	}
720
+	return result
693 721
 }
694 722
 
695 723
 func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName, remoteName string, localRepo map[string]string, indexEp string, sf *utils.StreamFormatter) error {
... ...
@@ -698,39 +730,54 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName
698 698
 	if err != nil {
699 699
 		return err
700 700
 	}
701
+	flattenedImgList := flatten(imgList)
701 702
 	out.Write(sf.FormatStatus("", "Sending image list"))
702 703
 
703 704
 	var repoData *registry.RepositoryData
704
-	repoData, err = r.PushImageJSONIndex(indexEp, remoteName, imgList, false, nil)
705
+	repoData, err = r.PushImageJSONIndex(indexEp, remoteName, flattenedImgList, false, nil)
705 706
 	if err != nil {
706 707
 		return err
707 708
 	}
708 709
 
709 710
 	for _, ep := range repoData.Endpoints {
710 711
 		out.Write(sf.FormatStatus("", "Pushing repository %s (%d tags)", localName, len(localRepo)))
711
-		// For each image within the repo, push them
712
-		for _, elem := range imgList {
713
-			if _, exists := repoData.ImgList[elem.ID]; exists {
714
-				out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", elem.ID))
715
-				continue
716
-			} else if r.LookupRemoteImage(elem.ID, ep, repoData.Tokens) {
717
-				out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", elem.ID))
718
-				continue
719
-			}
720
-			if checksum, err := srv.pushImage(r, out, remoteName, elem.ID, ep, repoData.Tokens, sf); err != nil {
721
-				// FIXME: Continue on error?
722
-				return err
723
-			} else {
724
-				elem.Checksum = checksum
725
-			}
726
-			out.Write(sf.FormatStatus("", "Pushing tags for rev [%s] on {%s}", elem.ID, ep+"repositories/"+remoteName+"/tags/"+elem.Tag))
727
-			if err := r.PushRegistryTag(remoteName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
728
-				return err
712
+		// This section can not be parallelized (each round depends on the previous one)
713
+		for _, round := range imgList {
714
+			// FIXME: This section can be parallelized
715
+			for _, elem := range round {
716
+				var pushTags func() error
717
+				pushTags = func() error {
718
+					out.Write(sf.FormatStatus("", "Pushing tags for rev [%s] on {%s}", elem.ID, ep+"repositories/"+remoteName+"/tags/"+elem.Tag))
719
+					if err := r.PushRegistryTag(remoteName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
720
+						return err
721
+					}
722
+					return nil
723
+				}
724
+				if _, exists := repoData.ImgList[elem.ID]; exists {
725
+					if err := pushTags(); err != nil {
726
+						return err
727
+					}
728
+					out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", elem.ID))
729
+					continue
730
+				} else if r.LookupRemoteImage(elem.ID, ep, repoData.Tokens) {
731
+					if err := pushTags(); err != nil {
732
+						return err
733
+					}
734
+					out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", elem.ID))
735
+					continue
736
+				}
737
+				if checksum, err := srv.pushImage(r, out, remoteName, elem.ID, ep, repoData.Tokens, sf); err != nil {
738
+					// FIXME: Continue on error?
739
+					return err
740
+				} else {
741
+					elem.Checksum = checksum
742
+				}
743
+				return pushTags()
729 744
 			}
730 745
 		}
731 746
 	}
732 747
 
733
-	if _, err := r.PushImageJSONIndex(indexEp, remoteName, imgList, true, repoData.Endpoints); err != nil {
748
+	if _, err := r.PushImageJSONIndex(indexEp, remoteName, flattenedImgList, true, repoData.Endpoints); err != nil {
734 749
 		return err
735 750
 	}
736 751
 
... ...
@@ -872,8 +919,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
872 872
 	if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
873 873
 		config.MemorySwap = -1
874 874
 	}
875
-	b := NewBuilder(srv.runtime)
876
-	container, err := b.Create(config)
875
+	container, err := srv.runtime.Create(config)
877 876
 	if err != nil {
878 877
 		if srv.runtime.graph.IsNotExist(err) {
879 878
 
... ...
@@ -1064,7 +1110,7 @@ func (srv *Server) ImageDelete(name string, autoPrune bool) ([]APIRmi, error) {
1064 1064
 func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error) {
1065 1065
 
1066 1066
 	// Retrieve all images
1067
-	images, err := srv.runtime.graph.All()
1067
+	images, err := srv.runtime.graph.Map()
1068 1068
 	if err != nil {
1069 1069
 		return nil, err
1070 1070
 	}
... ...
@@ -431,3 +431,57 @@ func TestRmi(t *testing.T) {
431 431
 		}
432 432
 	}
433 433
 }
434
+
435
+func TestImagesFilter(t *testing.T) {
436
+	runtime := mkRuntime(t)
437
+	defer nuke(runtime)
438
+
439
+	srv := &Server{runtime: runtime}
440
+
441
+	if err := srv.runtime.repositories.Set("utest", "tag1", unitTestImageName, false); err != nil {
442
+		t.Fatal(err)
443
+	}
444
+
445
+	if err := srv.runtime.repositories.Set("utest/docker", "tag2", unitTestImageName, false); err != nil {
446
+		t.Fatal(err)
447
+	}
448
+	if err := srv.runtime.repositories.Set("utest:5000/docker", "tag3", unitTestImageName, false); err != nil {
449
+		t.Fatal(err)
450
+	}
451
+
452
+	images, err := srv.Images(false, "utest*/*")
453
+	if err != nil {
454
+		t.Fatal(err)
455
+	}
456
+
457
+	if len(images) != 2 {
458
+		t.Fatal("incorrect number of matches returned")
459
+	}
460
+
461
+	images, err = srv.Images(false, "utest")
462
+	if err != nil {
463
+		t.Fatal(err)
464
+	}
465
+
466
+	if len(images) != 1 {
467
+		t.Fatal("incorrect number of matches returned")
468
+	}
469
+
470
+	images, err = srv.Images(false, "utest*")
471
+	if err != nil {
472
+		t.Fatal(err)
473
+	}
474
+
475
+	if len(images) != 1 {
476
+		t.Fatal("incorrect number of matches returned")
477
+	}
478
+
479
+	images, err = srv.Images(false, "*5000*/*")
480
+	if err != nil {
481
+		t.Fatal(err)
482
+	}
483
+
484
+	if len(images) != 1 {
485
+		t.Fatal("incorrect number of matches returned")
486
+	}
487
+}
... ...
@@ -27,10 +27,9 @@ func setupWorkingDirectory(workdir string) {
27 27
 	if workdir == "" {
28 28
 		return
29 29
 	}
30
-    syscall.Chdir(workdir)
30
+	syscall.Chdir(workdir)
31 31
 }
32 32
 
33
-
34 33
 // Takes care of dropping privileges to the desired user
35 34
 func changeUser(u string) {
36 35
 	if u == "" {
... ...
@@ -43,17 +43,42 @@ func RestoreTerminal(fd uintptr, state *State) error {
43 43
 	return err
44 44
 }
45 45
 
46
+func SaveState(fd uintptr) (*State, error) {
47
+	var oldState State
48
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
49
+		return nil, err
50
+	}
51
+
52
+	return &oldState, nil
53
+}
54
+
55
+func DisableEcho(fd uintptr, state *State) error {
56
+	newState := state.termios
57
+	newState.Lflag &^= syscall.ECHO
58
+
59
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
60
+		return err
61
+	}
62
+	handleInterrupt(fd, state)
63
+	return nil
64
+}
65
+
46 66
 func SetRawTerminal(fd uintptr) (*State, error) {
47 67
 	oldState, err := MakeRaw(fd)
48 68
 	if err != nil {
49 69
 		return nil, err
50 70
 	}
51
-	c := make(chan os.Signal, 1)
52
-	signal.Notify(c, os.Interrupt)
71
+	handleInterrupt(fd, oldState)
72
+	return oldState, err
73
+}
74
+
75
+func handleInterrupt(fd uintptr, state *State) {
76
+	sigchan := make(chan os.Signal, 1)
77
+	signal.Notify(sigchan, os.Interrupt)
78
+
53 79
 	go func() {
54
-		_ = <-c
55
-		RestoreTerminal(fd, oldState)
80
+		_ = <-sigchan
81
+		RestoreTerminal(fd, state)
56 82
 		os.Exit(0)
57 83
 	}()
58
-	return oldState, err
59 84
 }
... ...
@@ -2,11 +2,10 @@
2 2
 # vi: set ft=ruby :
3 3
 
4 4
 BOX_NAME = "docker-ci"
5
-BOX_URI = "http://files.vagrantup.com/precise64.box"
6
-AWS_AMI = "ami-d0f89fb9"
5
+BOX_URI = "http://cloud-images.ubuntu.com/vagrant/raring/current/raring-server-cloudimg-amd64-vagrant-disk1.box"
6
+AWS_AMI = "ami-10314d79"
7 7
 DOCKER_PATH = "/data/docker"
8 8
 CFG_PATH = "#{DOCKER_PATH}/testing/buildbot"
9
-BUILDBOT_IP = "192.168.33.41"
10 9
 on_vbox = File.file?("#{File.dirname(__FILE__)}/.vagrant/machines/default/virtualbox/id") | \
11 10
   Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty? & \
12 11
   (on_vbox=true; ARGV.each do |arg| on_vbox &&= !arg.downcase.start_with?("--provider") end; on_vbox)
... ...
@@ -16,16 +15,22 @@ Vagrant::Config.run do |config|
16 16
   # Setup virtual machine box. This VM configuration code is always executed.
17 17
   config.vm.box = BOX_NAME
18 18
   config.vm.box_url = BOX_URI
19
+  config.vm.forward_port 8010, 8010
19 20
   config.vm.share_folder "v-data", DOCKER_PATH, "#{File.dirname(__FILE__)}/.."
20
-  config.vm.network :hostonly, BUILDBOT_IP
21 21
 
22 22
 
23 23
   # Deploy buildbot and its dependencies if it was not done
24 24
   if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty?
25 25
     # Add memory limitation capabilities
26 26
     pkg_cmd = 'sed -Ei \'s/^(GRUB_CMDLINE_LINUX_DEFAULT)=.+/\\1="cgroup_enable=memory swapaccount=1 quiet"/\' /etc/default/grub; '
27
-    # Install new kernel
28
-    pkg_cmd << "apt-get update -qq; apt-get install -q -y linux-image-generic-lts-raring; "
27
+    # Adjust kernel
28
+    pkg_cmd << "apt-get update -qq; "
29
+    if on_vbox
30
+      pkg_cmd << "apt-get install -q -y linux-image-extra-`uname -r`; "
31
+    else
32
+      pkg_cmd << "apt-get install -q -y linux-image-generic; "
33
+    end
34
+
29 35
     # Deploy buildbot CI
30 36
     pkg_cmd << "apt-get install -q -y python-dev python-pip supervisor; " \
31 37
       "pip install -r #{CFG_PATH}/requirements.txt; " \
... ...
@@ -35,11 +40,15 @@ Vagrant::Config.run do |config|
35 35
         "#{ENV['SMTP_PWD']} #{ENV['EMAIL_RCP']}; " \
36 36
       "#{CFG_PATH}/setup_credentials.sh #{USER} " \
37 37
         "#{ENV['REGISTRY_USER']} #{ENV['REGISTRY_PWD']}; "
38
-    # Install docker dependencies
39
-    pkg_cmd << "apt-get install -q -y python-software-properties; " \
40
-      "add-apt-repository -y ppa:dotcloud/docker-golang/ubuntu; apt-get update -qq; " \
41
-      "DEBIAN_FRONTEND=noninteractive apt-get install -q -y lxc git mercurial golang-stable aufs-tools make; "
42
-    # Activate new kernel
38
+    # Install docker and testing dependencies
39
+    pkg_cmd << "curl -s https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz | " \
40
+      "  tar -v -C /usr/local -xz; ln -s /usr/local/go/bin/go /usr/bin/go; " \
41
+      "curl -s https://phantomjs.googlecode.com/files/phantomjs-1.9.1-linux-x86_64.tar.bz2 | " \
42
+      "  tar jx -C /usr/bin --strip-components=2 phantomjs-1.9.1-linux-x86_64/bin/phantomjs; " \
43
+      "DEBIAN_FRONTEND=noninteractive apt-get install -q -y lxc git mercurial aufs-tools make libfontconfig; " \
44
+      "export GOPATH=/data/docker-dependencies; go get -d github.com/dotcloud/docker; " \
45
+      "rm -rf ${GOPATH}/src/github.com/dotcloud/docker; "
46
+    # Activate new kernel options
43 47
     pkg_cmd << "shutdown -r +1; "
44 48
     config.vm.provision :shell, :inline => pkg_cmd
45 49
   end
46 50
new file mode 100644
... ...
@@ -0,0 +1,169 @@
0
+# This file is part of Buildbot.  Buildbot is free software: you can
1
+# redistribute it and/or modify it under the terms of the GNU General Public
2
+# License as published by the Free Software Foundation, version 2.
3
+#
4
+# This program is distributed in the hope that it will be useful, but WITHOUT
5
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
6
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
7
+# details.
8
+#
9
+# You should have received a copy of the GNU General Public License along with
10
+# this program; if not, write to the Free Software Foundation, Inc., 51
11
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
12
+#
13
+# Copyright Buildbot Team Members
14
+
15
+#!/usr/bin/env python
16
+"""
17
+github_buildbot.py is based on git_buildbot.py
18
+
19
+github_buildbot.py will determine the repository information from the JSON 
20
+HTTP POST it receives from github.com and build the appropriate repository.
21
+If your github repository is private, you must add a ssh key to the github
22
+repository for the user who initiated the build on the buildslave.
23
+
24
+"""
25
+
26
+import re
27
+import datetime
28
+from twisted.python import log
29
+import calendar
30
+
31
+try:
32
+    import json
33
+    assert json
34
+except ImportError:
35
+    import simplejson as json
36
+
37
+# python is silly about how it handles timezones
38
+class fixedOffset(datetime.tzinfo):
39
+    """
40
+    fixed offset timezone
41
+    """
42
+    def __init__(self, minutes, hours, offsetSign = 1):
43
+        self.minutes = int(minutes) * offsetSign
44
+        self.hours   = int(hours)   * offsetSign
45
+        self.offset  = datetime.timedelta(minutes = self.minutes,
46
+                                         hours   = self.hours)
47
+
48
+    def utcoffset(self, dt):
49
+        return self.offset
50
+
51
+    def dst(self, dt):
52
+        return datetime.timedelta(0)
53
+    
54
+def convertTime(myTestTimestamp):
55
+    #"1970-01-01T00:00:00+00:00"
56
+    # Normalize myTestTimestamp
57
+    if myTestTimestamp[-1] == 'Z':
58
+        myTestTimestamp = myTestTimestamp[:-1] + '-00:00'
59
+    matcher = re.compile(r'(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)([-+])(\d\d):(\d\d)')
60
+    result  = matcher.match(myTestTimestamp)
61
+    (year, month, day, hour, minute, second, offsetsign, houroffset, minoffset) = \
62
+        result.groups()
63
+    if offsetsign == '+':
64
+        offsetsign = 1
65
+    else:
66
+        offsetsign = -1
67
+    
68
+    offsetTimezone = fixedOffset( minoffset, houroffset, offsetsign )
69
+    myDatetime = datetime.datetime( int(year),
70
+                                    int(month),
71
+                                    int(day),
72
+                                    int(hour),
73
+                                    int(minute),
74
+                                    int(second),
75
+                                    0,
76
+                                    offsetTimezone)
77
+    return calendar.timegm( myDatetime.utctimetuple() )
78
+
79
+def getChanges(request, options = None):
80
+        """
81
+        Reponds only to POST events and starts the build process
82
+        
83
+        :arguments:
84
+            request
85
+                the http request object
86
+        """
87
+        payload = json.loads(request.args['payload'][0])
88
+	if 'pull_request' in payload:
89
+	    user = payload['repository']['owner']['login']
90
+	    repo = payload['repository']['name']
91
+            repo_url = payload['repository']['html_url']
92
+	else:
93
+	    user = payload['repository']['owner']['name']
94
+            repo = payload['repository']['name']
95
+            repo_url = payload['repository']['url']
96
+        project = request.args.get('project', None)
97
+        if project:
98
+            project = project[0]
99
+        elif project is None:
100
+            project = ''
101
+        # This field is unused:
102
+        #private = payload['repository']['private']
103
+        changes = process_change(payload, user, repo, repo_url, project)
104
+        log.msg("Received %s changes from github" % len(changes))
105
+        return (changes, 'git')
106
+
107
+def process_change(payload, user, repo, repo_url, project):
108
+        """
109
+        Consumes the JSON as a python object and actually starts the build.
110
+        
111
+        :arguments:
112
+            payload
113
+                Python Object that represents the JSON sent by GitHub Service
114
+                Hook.
115
+        """
116
+        changes = []
117
+	
118
+        newrev = payload['after'] if 'after' in payload else payload['pull_request']['head']['sha']
119
+        refname = payload['ref'] if 'ref' in payload else payload['pull_request']['head']['ref']
120
+
121
+        # We only care about regular heads, i.e. branches
122
+        match = re.match(r"^(refs\/heads\/|)([^/]+)$", refname)
123
+        if not match:
124
+            log.msg("Ignoring refname `%s': Not a branch" % refname)
125
+            return []
126
+
127
+        branch = match.groups()[1]
128
+        if re.match(r"^0*$", newrev):
129
+            log.msg("Branch `%s' deleted, ignoring" % branch)
130
+            return []
131
+        else: 
132
+	    if 'pull_request' in payload:
133
+		changes = [{
134
+		    'category'   : 'github_pullrequest',
135
+                    'who'        : user,
136
+                    'files'      : [],
137
+                    'comments'   : payload['pull_request']['title'], 
138
+                    'revision'   : newrev,
139
+                    'when'       : convertTime(payload['pull_request']['updated_at']),
140
+                    'branch'     : branch,
141
+                    'revlink'    : '{0}/commit/{1}'.format(repo_url,newrev),
142
+                    'repository' : repo_url,
143
+                    'project'  : project  }] 
144
+		return changes
145
+            for commit in payload['commits']:
146
+                files = []
147
+                if 'added' in commit:
148
+                    files.extend(commit['added'])
149
+                if 'modified' in commit:
150
+                    files.extend(commit['modified'])
151
+                if 'removed' in commit:
152
+                    files.extend(commit['removed'])
153
+                when =  convertTime( commit['timestamp'])
154
+                log.msg("New revision: %s" % commit['id'][:8])
155
+                chdict = dict(
156
+                    who      = commit['author']['name'] 
157
+                                + " <" + commit['author']['email'] + ">",
158
+                    files    = files,
159
+                    comments = commit['message'], 
160
+                    revision = commit['id'],
161
+                    when     = when,
162
+                    branch   = branch,
163
+                    revlink  = commit['url'], 
164
+                    repository = repo_url,
165
+                    project  = project)
166
+                changes.append(chdict) 
167
+            return changes
168
+        
... ...
@@ -22,7 +22,7 @@ GITHUB_DOCKER = 'github.com/dotcloud/docker'
22 22
 BUILDBOT_PATH = '/data/buildbot'
23 23
 DOCKER_PATH = '/data/docker'
24 24
 BUILDER_PATH = '/data/buildbot/slave/{0}/build'.format(BUILDER_NAME)
25
-DOCKER_BUILD_PATH = BUILDER_PATH + '/src/github.com/dotcloud/docker'
25
+PULL_REQUEST_PATH = '/data/buildbot/slave/pullrequest/build'
26 26
 
27 27
 # Credentials set by setup.sh and Vagrantfile
28 28
 BUILDBOT_PWD = ''
... ...
@@ -45,24 +45,40 @@ c['slavePortnum'] = PORT_MASTER
45 45
 
46 46
 # Schedulers
47 47
 c['schedulers'] = [ForceScheduler(name='trigger', builderNames=[BUILDER_NAME,
48
-    'registry','coverage'])]
48
+    'index','registry','coverage'])]
49 49
 c['schedulers'] += [SingleBranchScheduler(name="all",
50 50
     change_filter=filter.ChangeFilter(branch='master'), treeStableTimer=None,
51 51
     builderNames=[BUILDER_NAME])]
52
-c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['coverage','registry'],
52
+c['schedulers'] += [SingleBranchScheduler(name='pullrequest',
53
+    change_filter=filter.ChangeFilter(category='github_pullrequest'), treeStableTimer=None,
54
+    builderNames=['pullrequest'])]
55
+c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['coverage'],
53 56
     hour=0, minute=30)]
54
-
57
+c['schedulers'] += [Nightly(name='every4hrs', branch=None, builderNames=['registry','index'],
58
+    hour=range(0,24,4), minute=15)]
55 59
 
56 60
 # Builders
57 61
 # Docker commit test
58 62
 factory = BuildFactory()
59 63
 factory.addStep(ShellCommand(description='Docker',logEnviron=False,usePTY=True,
60
-    command=["sh", "-c", Interpolate("cd ..; rm -rf build; export GOPATH={0}; "
61
-    "go get -d {1}; cd {2}; git reset --hard %(src::revision:-unknown)s; "
62
-    "go test -v".format(BUILDER_PATH,GITHUB_DOCKER,DOCKER_BUILD_PATH))]))
64
+    command=["sh", "-c", Interpolate("cd ..; rm -rf build; mkdir build; "
65
+    "cp -r {2}-dependencies/src {0}; export GOPATH={0}; go get {3}; cd {1}; "
66
+    "git reset --hard %(src::revision)s; go test -v".format(
67
+    BUILDER_PATH, BUILDER_PATH+'/src/'+GITHUB_DOCKER, DOCKER_PATH, GITHUB_DOCKER))]))
63 68
 c['builders'] = [BuilderConfig(name=BUILDER_NAME,slavenames=['buildworker'],
64 69
     factory=factory)]
65 70
 
71
+# Docker pull request test
72
+factory = BuildFactory()
73
+factory.addStep(ShellCommand(description='pull_request',logEnviron=False,usePTY=True,
74
+    command=["sh", "-c", Interpolate("cd ..; rm -rf build; mkdir build; "
75
+    "cp -r {2}-dependencies/src {0}; export GOPATH={0}; go get {3}; cd {1}; "
76
+    "git fetch %(src::repository)s %(src::branch)s:PR-%(src::branch)s; "
77
+    "git checkout %(src::revision)s; git rebase master; go test -v".format(
78
+    PULL_REQUEST_PATH, PULL_REQUEST_PATH+'/src/'+GITHUB_DOCKER, DOCKER_PATH, GITHUB_DOCKER))]))
79
+c['builders'] += [BuilderConfig(name='pullrequest',slavenames=['buildworker'],
80
+    factory=factory)]
81
+
66 82
 # Docker coverage test
67 83
 coverage_cmd = ('GOPATH=`pwd` go get -d github.com/dotcloud/docker\n'
68 84
     'GOPATH=`pwd` go get github.com/axw/gocov/gocov\n'
... ...
@@ -75,16 +91,24 @@ factory.addStep(ShellCommand(description='Coverage',logEnviron=False,usePTY=True
75 75
 c['builders'] += [BuilderConfig(name='coverage',slavenames=['buildworker'],
76 76
     factory=factory)]
77 77
 
78
-# Registry Functionaltest builder
78
+# Registry functional test
79 79
 factory = BuildFactory()
80 80
 factory.addStep(ShellCommand(description='registry', logEnviron=False,
81 81
     command='. {0}/master/credentials.cfg; '
82 82
     '{1}/testing/functionaltests/test_registry.sh'.format(BUILDBOT_PATH,
83 83
     DOCKER_PATH), usePTY=True))
84
-
85 84
 c['builders'] += [BuilderConfig(name='registry',slavenames=['buildworker'],
86 85
     factory=factory)]
87 86
 
87
+# Index functional test
88
+factory = BuildFactory()
89
+factory.addStep(ShellCommand(description='index', logEnviron=False,
90
+    command='. {0}/master/credentials.cfg; '
91
+    '{1}/testing/functionaltests/test_index.py'.format(BUILDBOT_PATH,
92
+    DOCKER_PATH), usePTY=True))
93
+c['builders'] += [BuilderConfig(name='index',slavenames=['buildworker'],
94
+    factory=factory)]
95
+
88 96
 
89 97
 # Status
90 98
 authz_cfg = authz.Authz(auth=auth.BasicAuth([(TEST_USER, TEST_PWD)]),
... ...
@@ -5,3 +5,5 @@ buildbot_slave==0.8.7p1
5 5
 nose==1.2.1
6 6
 requests==1.1.0
7 7
 flask==0.10.1
8
+simplejson==2.3.2
9
+selenium==2.35.0
... ...
@@ -36,6 +36,9 @@ run "sed -i -E 's#(SMTP_PWD = ).+#\1\"$SMTP_PWD\"#' master/master.cfg"
36 36
 run "sed -i -E 's#(EMAIL_RCP = ).+#\1\"$EMAIL_RCP\"#' master/master.cfg"
37 37
 run "buildslave create-slave slave $SLAVE_SOCKET $SLAVE_NAME $BUILDBOT_PWD"
38 38
 
39
+# Patch github webstatus to capture pull requests
40
+cp $CFG_PATH/github.py /usr/local/lib/python2.7/dist-packages/buildbot/status/web/hooks
41
+
39 42
 # Allow buildbot subprocesses (docker tests) to properly run in containers,
40 43
 # in particular with docker -u
41 44
 run "sed -i 's/^umask = None/umask = 000/' slave/buildbot.tac"
42 45
new file mode 100755
... ...
@@ -0,0 +1,61 @@
0
+#!/usr/bin/python
1
+
2
+import os
3
+username, password = os.environ['DOCKER_CREDS'].split(':')
4
+
5
+from selenium import webdriver
6
+from selenium.webdriver.common.by import By
7
+from selenium.webdriver.common.keys import Keys
8
+from selenium.webdriver.support.ui import Select
9
+from selenium.common.exceptions import NoSuchElementException
10
+import unittest, time, re
11
+
12
+class Docker(unittest.TestCase):
13
+    def setUp(self):
14
+        self.driver = webdriver.PhantomJS()
15
+        self.driver.implicitly_wait(30)
16
+        self.base_url = "http://www.docker.io/"
17
+        self.verificationErrors = []
18
+        self.accept_next_alert = True
19
+
20
+    def test_docker(self):
21
+        driver = self.driver
22
+        print "Login into {0} as login user {1} ...".format(self.base_url,username)
23
+        driver.get(self.base_url + "/")
24
+        driver.find_element_by_link_text("INDEX").click()
25
+        driver.find_element_by_link_text("login").click()
26
+        driver.find_element_by_id("id_username").send_keys(username)
27
+        driver.find_element_by_id("id_password").send_keys(password)
28
+        print "Checking login user ..."
29
+        driver.find_element_by_css_selector("input[type=\"submit\"]").click()
30
+        try: self.assertEqual("test", driver.find_element_by_css_selector("h3").text)
31
+        except AssertionError as e: self.verificationErrors.append(str(e))
32
+        print "Login user {0} found".format(username)
33
+
34
+    def is_element_present(self, how, what):
35
+        try: self.driver.find_element(by=how, value=what)
36
+        except NoSuchElementException, e: return False
37
+        return True
38
+
39
+    def is_alert_present(self):
40
+        try: self.driver.switch_to_alert()
41
+        except NoAlertPresentException, e: return False
42
+        return True
43
+
44
+    def close_alert_and_get_its_text(self):
45
+        try:
46
+            alert = self.driver.switch_to_alert()
47
+            alert_text = alert.text
48
+            if self.accept_next_alert:
49
+                alert.accept()
50
+            else:
51
+                alert.dismiss()
52
+            return alert_text
53
+        finally: self.accept_next_alert = True
54
+
55
+    def tearDown(self):
56
+        self.driver.quit()
57
+        self.assertEqual([], self.verificationErrors)
58
+
59
+if __name__ == "__main__":
60
+    unittest.main()
... ...
@@ -14,7 +14,6 @@ import (
14 14
 	"net/http"
15 15
 	"os"
16 16
 	"os/exec"
17
-	"os/user"
18 17
 	"path/filepath"
19 18
 	"runtime"
20 19
 	"strconv"
... ...
@@ -781,21 +780,37 @@ func GetResolvConf() ([]byte, error) {
781 781
 // CheckLocalDns looks into the /etc/resolv.conf,
782 782
 // it returns true if there is a local nameserver or if there is no nameserver.
783 783
 func CheckLocalDns(resolvConf []byte) bool {
784
-	if !bytes.Contains(resolvConf, []byte("nameserver")) {
784
+	var parsedResolvConf = StripComments(resolvConf, []byte("#"))
785
+	if !bytes.Contains(parsedResolvConf, []byte("nameserver")) {
785 786
 		return true
786 787
 	}
787
-
788 788
 	for _, ip := range [][]byte{
789 789
 		[]byte("127.0.0.1"),
790 790
 		[]byte("127.0.1.1"),
791 791
 	} {
792
-		if bytes.Contains(resolvConf, ip) {
792
+		if bytes.Contains(parsedResolvConf, ip) {
793 793
 			return true
794 794
 		}
795 795
 	}
796 796
 	return false
797 797
 }
798 798
 
799
+// StripComments parses input into lines and strips away comments.
800
+func StripComments(input []byte, commentMarker []byte) []byte {
801
+	lines := bytes.Split(input, []byte("\n"))
802
+	var output []byte
803
+	for _, currentLine := range lines {
804
+		var commentIndex = bytes.Index(currentLine, commentMarker)
805
+		if commentIndex == -1 {
806
+			output = append(output, currentLine...)
807
+		} else {
808
+			output = append(output, currentLine[:commentIndex]...)
809
+		}
810
+		output = append(output, []byte("\n")...)
811
+	}
812
+	return output
813
+}
814
+
799 815
 func ParseHost(host string, port int, addr string) string {
800 816
 	if strings.HasPrefix(addr, "unix://") {
801 817
 		return addr
... ...
@@ -851,10 +866,18 @@ func ParseRepositoryTag(repos string) (string, string) {
851 851
 	return repos, ""
852 852
 }
853 853
 
854
+type User struct {
855
+	Uid      string // user id
856
+	Gid      string // primary group id
857
+	Username string
858
+	Name     string
859
+	HomeDir  string
860
+}
861
+
854 862
 // UserLookup check if the given username or uid is present in /etc/passwd
855 863
 // and returns the user struct.
856 864
 // If the username is not found, an error is returned.
857
-func UserLookup(uid string) (*user.User, error) {
865
+func UserLookup(uid string) (*User, error) {
858 866
 	file, err := ioutil.ReadFile("/etc/passwd")
859 867
 	if err != nil {
860 868
 		return nil, err
... ...
@@ -862,7 +885,7 @@ func UserLookup(uid string) (*user.User, error) {
862 862
 	for _, line := range strings.Split(string(file), "\n") {
863 863
 		data := strings.Split(line, ":")
864 864
 		if len(data) > 5 && (data[0] == uid || data[2] == uid) {
865
-			return &user.User{
865
+			return &User{
866 866
 				Uid:      data[2],
867 867
 				Gid:      data[3],
868 868
 				Username: data[0],
... ...
@@ -873,3 +896,128 @@ func UserLookup(uid string) (*user.User, error) {
873 873
 	}
874 874
 	return nil, fmt.Errorf("User not found in /etc/passwd")
875 875
 }
876
+
877
+type DependencyGraph struct {
878
+	nodes map[string]*DependencyNode
879
+}
880
+
881
+type DependencyNode struct {
882
+	id   string
883
+	deps map[*DependencyNode]bool
884
+}
885
+
886
+func NewDependencyGraph() DependencyGraph {
887
+	return DependencyGraph{
888
+		nodes: map[string]*DependencyNode{},
889
+	}
890
+}
891
+
892
+func (graph *DependencyGraph) addNode(node *DependencyNode) string {
893
+	if graph.nodes[node.id] == nil {
894
+		graph.nodes[node.id] = node
895
+	}
896
+	return node.id
897
+}
898
+
899
+func (graph *DependencyGraph) NewNode(id string) string {
900
+	if graph.nodes[id] != nil {
901
+		return id
902
+	}
903
+	nd := &DependencyNode{
904
+		id:   id,
905
+		deps: map[*DependencyNode]bool{},
906
+	}
907
+	graph.addNode(nd)
908
+	return id
909
+}
910
+
911
+func (graph *DependencyGraph) AddDependency(node, to string) error {
912
+	if graph.nodes[node] == nil {
913
+		return fmt.Errorf("Node %s does not belong to this graph", node)
914
+	}
915
+
916
+	if graph.nodes[to] == nil {
917
+		return fmt.Errorf("Node %s does not belong to this graph", to)
918
+	}
919
+
920
+	if node == to {
921
+		return fmt.Errorf("Dependency loops are forbidden!")
922
+	}
923
+
924
+	graph.nodes[node].addDependency(graph.nodes[to])
925
+	return nil
926
+}
927
+
928
+func (node *DependencyNode) addDependency(to *DependencyNode) bool {
929
+	node.deps[to] = true
930
+	return node.deps[to]
931
+}
932
+
933
+func (node *DependencyNode) Degree() int {
934
+	return len(node.deps)
935
+}
936
+
937
+// The magic happens here ::
938
+func (graph *DependencyGraph) GenerateTraversalMap() ([][]string, error) {
939
+	Debugf("Generating traversal map. Nodes: %d", len(graph.nodes))
940
+	result := [][]string{}
941
+	processed := map[*DependencyNode]bool{}
942
+	// As long as we haven't processed all nodes...
943
+	for len(processed) < len(graph.nodes) {
944
+		// Use a temporary buffer for processed nodes, otherwise
945
+		// nodes that depend on each other could end up in the same round.
946
+		tmp_processed := []*DependencyNode{}
947
+		for _, node := range graph.nodes {
948
+			// If the node has more dependencies than what we have cleared,
949
+			// it won't be valid for this round.
950
+			if node.Degree() > len(processed) {
951
+				continue
952
+			}
953
+			// If it's already processed, get to the next one
954
+			if processed[node] {
955
+				continue
956
+			}
957
+			// It's not been processed yet and has 0 deps. Add it!
958
+			// (this is a shortcut for what we're doing below)
959
+			if node.Degree() == 0 {
960
+				tmp_processed = append(tmp_processed, node)
961
+				continue
962
+			}
963
+			// If at least one dep hasn't been processed yet, we can't
964
+			// add it.
965
+			ok := true
966
+			for dep := range node.deps {
967
+				if !processed[dep] {
968
+					ok = false
969
+					break
970
+				}
971
+			}
972
+			// All deps have already been processed. Add it!
973
+			if ok {
974
+				tmp_processed = append(tmp_processed, node)
975
+			}
976
+		}
977
+		Debugf("Round %d: found %d available nodes", len(result), len(tmp_processed))
978
+		// If no progress has been made this round,
979
+		// that means we have circular dependencies.
980
+		if len(tmp_processed) == 0 {
981
+			return nil, fmt.Errorf("Could not find a solution to this dependency graph")
982
+		}
983
+		round := []string{}
984
+		for _, nd := range tmp_processed {
985
+			round = append(round, nd.id)
986
+			processed[nd] = true
987
+		}
988
+		result = append(result, round)
989
+	}
990
+	return result, nil
991
+}
992
+
993
+// An StatusError reports an unsuccessful exit by a command.
994
+type StatusError struct {
995
+	Status int
996
+}
997
+
998
+func (e *StatusError) Error() string {
999
+	return fmt.Sprintf("Status: %d", e.Status)
1000
+}
... ...
@@ -323,6 +323,16 @@ func TestCheckLocalDns(t *testing.T) {
323 323
 nameserver 10.0.2.3
324 324
 search dotcloud.net`: false,
325 325
 		`# Dynamic
326
+#nameserver 127.0.0.1
327
+nameserver 10.0.2.3
328
+search dotcloud.net`: false,
329
+		`# Dynamic
330
+nameserver 10.0.2.3 #not used 127.0.1.1
331
+search dotcloud.net`: false,
332
+		`# Dynamic
333
+#nameserver 10.0.2.3
334
+#search dotcloud.net`: true,
335
+		`# Dynamic
326 336
 nameserver 127.0.0.1
327 337
 search dotcloud.net`: true,
328 338
 		`# Dynamic
... ...
@@ -355,3 +365,59 @@ func TestParseRelease(t *testing.T) {
355 355
 	assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: "1"}, 0)
356 356
 	assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "19-generic"}, 0)
357 357
 }
358
+
359
+func TestDependencyGraphCircular(t *testing.T) {
360
+	g1 := NewDependencyGraph()
361
+	a := g1.NewNode("a")
362
+	b := g1.NewNode("b")
363
+	g1.AddDependency(a, b)
364
+	g1.AddDependency(b, a)
365
+	res, err := g1.GenerateTraversalMap()
366
+	if res != nil {
367
+		t.Fatalf("Expected nil result")
368
+	}
369
+	if err == nil {
370
+		t.Fatalf("Expected error (circular graph can not be resolved)")
371
+	}
372
+}
373
+
374
+func TestDependencyGraph(t *testing.T) {
375
+	g1 := NewDependencyGraph()
376
+	a := g1.NewNode("a")
377
+	b := g1.NewNode("b")
378
+	c := g1.NewNode("c")
379
+	d := g1.NewNode("d")
380
+	g1.AddDependency(b, a)
381
+	g1.AddDependency(c, a)
382
+	g1.AddDependency(d, c)
383
+	g1.AddDependency(d, b)
384
+	res, err := g1.GenerateTraversalMap()
385
+
386
+	if err != nil {
387
+		t.Fatalf("%s", err)
388
+	}
389
+
390
+	if res == nil {
391
+		t.Fatalf("Unexpected nil result")
392
+	}
393
+
394
+	if len(res) != 3 {
395
+		t.Fatalf("Expected map of length 3, found %d instead", len(res))
396
+	}
397
+
398
+	if len(res[0]) != 1 || res[0][0] != "a" {
399
+		t.Fatalf("Expected [a], found %v instead", res[0])
400
+	}
401
+
402
+	if len(res[1]) != 2 {
403
+		t.Fatalf("Expected 2 nodes for step 2, found %d", len(res[1]))
404
+	}
405
+
406
+	if (res[1][0] != "b" && res[1][1] != "b") || (res[1][0] != "c" && res[1][1] != "c") {
407
+		t.Fatalf("Expected [b, c], found %v instead", res[1])
408
+	}
409
+
410
+	if len(res[2]) != 1 || res[2][0] != "d" {
411
+		t.Fatalf("Expected [d], found %v instead", res[2])
412
+	}
413
+}
... ...
@@ -101,7 +101,7 @@ func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConf
101 101
 	if config.Image == "_" {
102 102
 		config.Image = GetTestImage(r).ID
103 103
 	}
104
-	c, err := NewBuilder(r).Create(config)
104
+	c, err := r.Create(config)
105 105
 	if err != nil {
106 106
 		return nil, nil, err
107 107
 	}