Przeglądaj źródła

Merge pull request #2428 from dotcloud/bump_0.6.5

Bump to version 0.6.5
Michael Crosby 11 lat temu
rodzic
commit
4f6ce1ba9e
100 zmienionych plików z 2744 dodań i 1758 usunięć
  1. 1 0
      .gitignore
  2. 2 0
      AUTHORS
  3. 29 0
      CHANGELOG.md
  4. 5 7
      Dockerfile
  5. 2 3
      NOTICE
  6. 6 6
      README.md
  7. 1 1
      VERSION
  8. 44 9
      api.go
  9. 9 17
      api_params.go
  10. 42 27
      api_test.go
  11. 4 1
      auth/auth_test.go
  12. 16 6
      buildfile.go
  13. 6 24
      buildfile_test.go
  14. 253 113
      commands.go
  15. 81 16
      commands_test.go
  16. 18 0
      config.go
  17. 275 85
      container.go
  18. 91 40
      container_test.go
  19. 0 1
      contrib/brew/.gitignore
  20. 0 78
      contrib/brew/README.md
  21. 0 1
      contrib/brew/brew/__init__.py
  22. 0 185
      contrib/brew/brew/brew.py
  23. 0 63
      contrib/brew/brew/git.py
  24. 0 35
      contrib/brew/docker-brew
  25. 0 2
      contrib/brew/requirements.txt
  26. 0 22
      contrib/brew/setup.py
  27. 27 0
      contrib/host-integration/Dockerfile.dev
  28. 4 0
      contrib/host-integration/Dockerfile.min
  29. 130 0
      contrib/host-integration/manager.go
  30. 53 0
      contrib/host-integration/manager.sh
  31. 20 0
      contrib/host-integration/manager/systemd
  32. 15 0
      contrib/host-integration/manager/upstart
  33. 13 0
      contrib/init/openrc/docker.confd
  34. 31 0
      contrib/init/openrc/docker.initd
  35. 13 0
      contrib/init/systemd/docker.service
  36. 85 0
      contrib/init/sysvinit/docker
  37. 10 0
      contrib/init/upstart/docker.conf
  38. 12 10
      contrib/mkimage-arch.sh
  39. 1 1
      contrib/mkimage-busybox.sh
  40. 0 83
      contrib/mkimage-debian.sh
  41. 233 0
      contrib/mkimage-debootstrap.sh
  42. 1 1
      contrib/mkimage-unittest.sh
  43. 2 0
      contrib/vim-syntax/syntax/dockerfile.vim
  44. 54 26
      docker/docker.go
  45. 16 0
      dockerinit/dockerinit.go
  46. 6 1
      docs/Dockerfile
  47. 5 4
      docs/README.md
  48. 21 16
      docs/sources/api/docker_remote_api_v1.0.rst
  49. 31 26
      docs/sources/api/docker_remote_api_v1.1.rst
  50. 22 17
      docs/sources/api/docker_remote_api_v1.2.rst
  51. 22 17
      docs/sources/api/docker_remote_api_v1.3.rst
  52. 10 5
      docs/sources/api/docker_remote_api_v1.4.rst
  53. 23 18
      docs/sources/api/docker_remote_api_v1.5.rst
  54. 16 6
      docs/sources/api/docker_remote_api_v1.6.rst
  55. 1 1
      docs/sources/api/index.rst
  56. 702 38
      docs/sources/commandline/cli.rst
  57. 0 59
      docs/sources/commandline/command/attach.rst
  58. 0 65
      docs/sources/commandline/command/build.rst
  59. 0 52
      docs/sources/commandline/command/commit.rst
  60. 0 14
      docs/sources/commandline/command/cp.rst
  61. 0 13
      docs/sources/commandline/command/diff.rst
  62. 0 34
      docs/sources/commandline/command/events.rst
  63. 0 13
      docs/sources/commandline/command/export.rst
  64. 0 13
      docs/sources/commandline/command/history.rst
  65. 0 26
      docs/sources/commandline/command/images.rst
  66. 0 44
      docs/sources/commandline/command/import.rst
  67. 0 13
      docs/sources/commandline/command/info.rst
  68. 0 23
      docs/sources/commandline/command/insert.rst
  69. 0 13
      docs/sources/commandline/command/inspect.rst
  70. 0 13
      docs/sources/commandline/command/kill.rst
  71. 0 24
      docs/sources/commandline/command/login.rst
  72. 0 13
      docs/sources/commandline/command/logs.rst
  73. 0 13
      docs/sources/commandline/command/port.rst
  74. 0 17
      docs/sources/commandline/command/ps.rst
  75. 0 13
      docs/sources/commandline/command/pull.rst
  76. 0 13
      docs/sources/commandline/command/push.rst
  77. 0 13
      docs/sources/commandline/command/restart.rst
  78. 0 13
      docs/sources/commandline/command/rm.rst
  79. 0 13
      docs/sources/commandline/command/rmi.rst
  80. 0 85
      docs/sources/commandline/command/run.rst
  81. 0 14
      docs/sources/commandline/command/search.rst
  82. 0 13
      docs/sources/commandline/command/start.rst
  83. 0 15
      docs/sources/commandline/command/stop.rst
  84. 0 15
      docs/sources/commandline/command/tag.rst
  85. 0 13
      docs/sources/commandline/command/top.rst
  86. 0 7
      docs/sources/commandline/command/version.rst
  87. 0 13
      docs/sources/commandline/command/wait.rst
  88. 0 0
      docs/sources/commandline/docker_images.gif
  89. 2 33
      docs/sources/commandline/index.rst
  90. 14 1
      docs/sources/contributing/contributing.rst
  91. 1 1
      docs/sources/examples/hello_world.rst
  92. 2 1
      docs/sources/examples/index.rst
  93. 126 0
      docs/sources/examples/linking_into_redis.rst
  94. 0 2
      docs/sources/index.rst
  95. 1 1
      docs/sources/installation/kernel.rst
  96. 4 2
      docs/sources/installation/ubuntulinux.rst
  97. 1 1
      docs/sources/use/baseimages.rst
  98. 3 1
      docs/sources/use/basics.rst
  99. 1 1
      docs/sources/use/builder.rst
  100. 125 0
      docs/sources/use/host_integration.rst

+ 1 - 0
.gitignore

@@ -17,3 +17,4 @@ docs/_templates
 bundles/
 bundles/
 .hg/
 .hg/
 .git/
 .git/
+vendor/pkg/

+ 2 - 0
AUTHORS

@@ -151,6 +151,7 @@ Roberto Hashioka <roberto_hashioka@hotmail.com>
 Ryan Fowler <rwfowler@gmail.com>
 Ryan Fowler <rwfowler@gmail.com>
 Sam Alba <sam.alba@gmail.com>
 Sam Alba <sam.alba@gmail.com>
 Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
 Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
+Scott Bessler <scottbessler@gmail.com>
 Sean P. Kane <skane@newrelic.com>
 Sean P. Kane <skane@newrelic.com>
 Shawn Siefkas <shawn.siefkas@meredith.com>
 Shawn Siefkas <shawn.siefkas@meredith.com>
 Shih-Yuan Lee <fourdollars@gmail.com>
 Shih-Yuan Lee <fourdollars@gmail.com>
@@ -185,4 +186,5 @@ Vladimir Kirillov <proger@wilab.org.ua>
 Walter Stanish <walter@pratyeka.org>
 Walter Stanish <walter@pratyeka.org>
 Wes Morgan <cap10morgan@gmail.com>
 Wes Morgan <cap10morgan@gmail.com>
 Will Dietz <w@wdtz.org>
 Will Dietz <w@wdtz.org>
+Yang Bai <hamo.by@gmail.com>
 Zaiste! <oh@zaiste.net>
 Zaiste! <oh@zaiste.net>

+ 29 - 0
CHANGELOG.md

@@ -1,5 +1,34 @@
 # Changelog
 # Changelog
 
 
+## 0.6.5 (2013-10-29)
++ Runtime: Containers can now be named
++ Runtime: Containers can now be linked together for service discovery
++ Runtime: 'run -a', 'start -a' and 'attach' can forward signals to the container for better integration with process supervisors
++ Runtime: Automatically start crashed containers after a reboot
++ Runtime: Expose IP, port, and proto as separate environment vars for container links
+* Runtime: Allow ports to be published to specific ips
+* Runtime: Prohibit inter-container communication by default
+* Documentation: Fix the flags for nc in example
+- Client: Only pass stdin to hijack when needed to avoid closed pipe errors
+* Testing: Remove warnings and prevent mount issues
+* Client: Use less reflection in command-line method invocation
+- Runtime: Ignore ErrClosedPipe for stdin in Container.Attach
+- Testing: Change logic for tty resize to avoid warning in tests
+- Client: Monitor the tty size after starting the container, not prior
+- Hack: Update install.sh with $sh_c to get sudo/su for modprobe
+- Client: Remove useless os.Exit() calls after log.Fatal
+* Hack: Update all the mkimage scripts to use --numeric-owner as a tar argument
+* Hack: Update hack/release.sh process to automatically invoke hack/make.sh and bail on build and test issues
++ Hack: Add initial init scripts library and a safer Ubuntu packaging script that works for Debian
+- Runtime: Fix untag during removal of images 
+- Runtime: Remove unused field kernelVersion 
+* Hack: Add -p option to invoke debootstrap with http_proxy
+- Builder: Fix race condition in docker build with verbose output
+- Registry: Fix content-type for PushImageJSONIndex method
+* Runtime: Fix issue when mounting subdirectories of /mnt in container
+* Runtime: Check return value of syscall.Chdir when changing working directory inside dockerinit
+* Contrib: Improve helper tools to generate debian and Arch linux server images
+
 ## 0.6.4 (2013-10-16)
 ## 0.6.4 (2013-10-16)
 - Runtime: Add cleanup of container when Start() fails
 - Runtime: Add cleanup of container when Start() fails
 - Testing: Catch errClosing error when TCP and UDP proxies are terminated
 - Testing: Catch errClosing error when TCP and UDP proxies are terminated

+ 5 - 7
Dockerfile

@@ -33,15 +33,13 @@ run	apt-get update
 run	apt-get install -y -q curl
 run	apt-get install -y -q curl
 run	apt-get install -y -q git
 run	apt-get install -y -q git
 run	apt-get install -y -q mercurial
 run	apt-get install -y -q mercurial
-run	apt-get install -y -q build-essential
+run apt-get install -y -q build-essential libsqlite3-dev
 
 
-# Install Go from source (for eventual cross-compiling)
-env	CGO_ENABLED 0
-run	curl -s https://go.googlecode.com/files/go1.1.2.src.tar.gz | tar -v -C / -xz && mv /go /goroot
-run	cd /goroot/src && ./make.bash
-env GOROOT	/goroot
-env	PATH	$PATH:/goroot/bin
+# Install Go
+run	curl -s https://go.googlecode.com/files/go1.2rc2.src.tar.gz | tar -v -C /usr/local -xz
+env	PATH	/usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
 env	GOPATH	/go:/go/src/github.com/dotcloud/docker/vendor
 env	GOPATH	/go:/go/src/github.com/dotcloud/docker/vendor
+run cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
 
 
 # Ubuntu stuff
 # Ubuntu stuff
 run	apt-get install -y -q ruby1.9.3 rubygems libffi-dev
 run	apt-get install -y -q ruby1.9.3 rubygems libffi-dev

+ 2 - 3
NOTICE

@@ -1,8 +1,7 @@
 Docker
 Docker
-Copyright 2012-2013 dotCloud, inc.
+Copyright 2012-2013 Docker, Inc.
 
 
-This product includes software developed at dotCloud,
-inc. (http://www.dotcloud.com).
+This product includes software developed at Docker, Inc. (http://www.docker.com).
 
 
 This product contains software (https://github.com/kr/pty) developed
 This product contains software (https://github.com/kr/pty) developed
 by Keith Rarick, licensed under the MIT License.
 by Keith Rarick, licensed under the MIT License.

+ 6 - 6
README.md

@@ -193,10 +193,10 @@ wrong or incomplete.
 *Brought to you courtesy of our legal counsel. For more context,
 *Brought to you courtesy of our legal counsel. For more context,
 please see the Notice document.*
 please see the Notice document.*
 
 
-Transfers of Docker shall be in accordance with applicable export
-controls of any country and all other applicable legal requirements.
-Docker shall not be distributed or downloaded to or in Cuba, Iran,
-North Korea, Sudan or Syria and shall not be distributed or downloaded
-to any person on the Denied Persons List administered by the U.S.
-Department of Commerce.
+Transfers of Docker shall be in accordance with applicable export controls 
+of any country and all other applicable legal requirements. Without limiting the 
+foregoing, Docker shall not be distributed or downloaded to any individual or 
+location if such distribution or download would violate the applicable US 
+government export regulations. 
 
 
+For more information, please see http://www.bis.doc.gov

+ 1 - 1
VERSION

@@ -1 +1 @@
-0.6.4
+0.6.5

+ 44 - 9
api.go

@@ -42,6 +42,9 @@ func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
 
 
 //If we don't do this, POST method without Content-type (even with empty body) will fail
 //If we don't do this, POST method without Content-type (even with empty body) will fail
 func parseForm(r *http.Request) error {
 func parseForm(r *http.Request) error {
+	if r == nil {
+		return nil
+	}
 	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
 	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
 		return err
 		return err
 	}
 	}
@@ -69,12 +72,12 @@ func httpError(w http.ResponseWriter, err error) {
 		statusCode = http.StatusUnauthorized
 		statusCode = http.StatusUnauthorized
 	} else if strings.Contains(err.Error(), "hasn't been activated") {
 	} else if strings.Contains(err.Error(), "hasn't been activated") {
 		statusCode = http.StatusForbidden
 		statusCode = http.StatusForbidden
-	}	
-	
+	}
+
 	if err != nil {
 	if err != nil {
 		utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
 		utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
-		http.Error(w, err.Error(), statusCode)		
-	}	
+		http.Error(w, err.Error(), statusCode)
+	}
 }
 }
 
 
 func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
 func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
@@ -135,8 +138,23 @@ func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
+	if err := parseForm(r); err != nil {
+		return err
+	}
 	name := vars["name"]
 	name := vars["name"]
-	if err := srv.ContainerKill(name); err != nil {
+
+	signal := 0
+	if r != nil {
+		s := r.Form.Get("signal")
+		if s != "" {
+			if s, err := strconv.Atoi(s); err != nil {
+				return err
+			} else {
+				signal = s
+			}
+		}
+	}
+	if err := srv.ContainerKill(name, signal); err != nil {
 		return err
 		return err
 	}
 	}
 	w.WriteHeader(http.StatusNoContent)
 	w.WriteHeader(http.StatusNoContent)
@@ -503,8 +521,12 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
 }
 }
 
 
 func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if err := parseForm(r); err != nil {
+		return nil
+	}
 	config := &Config{}
 	config := &Config{}
 	out := &APIRun{}
 	out := &APIRun{}
+	name := r.Form.Get("name")
 
 
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
 		return err
 		return err
@@ -515,16 +537,19 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
 		return err
 		return err
 	}
 	}
 
 
-	if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
+	if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
 		out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns))
 		out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns))
 		config.Dns = defaultDns
 		config.Dns = defaultDns
 	}
 	}
 
 
-	id, err := srv.ContainerCreate(config)
+	id, warnings, err := srv.ContainerCreate(config, name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	out.ID = id
 	out.ID = id
+	for _, warning := range warnings {
+		out.Warnings = append(out.Warnings, warning)
+	}
 
 
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
 		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
 		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
@@ -570,12 +595,17 @@ func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *ht
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
 	name := vars["name"]
 	name := vars["name"]
+
 	removeVolume, err := getBoolParam(r.Form.Get("v"))
 	removeVolume, err := getBoolParam(r.Form.Get("v"))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+	removeLink, err := getBoolParam(r.Form.Get("link"))
+	if err != nil {
+		return err
+	}
 
 
-	if err := srv.ContainerDestroy(name, removeVolume); err != nil {
+	if err := srv.ContainerDestroy(name, removeVolume, removeLink); err != nil {
 		return err
 		return err
 	}
 	}
 	w.WriteHeader(http.StatusNoContent)
 	w.WriteHeader(http.StatusNoContent)
@@ -622,6 +652,10 @@ func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
 	name := vars["name"]
 	name := vars["name"]
+	// Register any links from the host config before starting the container
+	if err := srv.RegisterLinks(name, hostConfig); err != nil {
+		return err
+	}
 	if err := srv.ContainerStart(name, hostConfig); err != nil {
 	if err := srv.ContainerStart(name, hostConfig); err != nil {
 		return err
 		return err
 	}
 	}
@@ -655,6 +689,7 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
 	name := vars["name"]
 	name := vars["name"]
+
 	status, err := srv.ContainerWait(name)
 	status, err := srv.ContainerWait(name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -975,7 +1010,7 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s
 		if err != nil {
 		if err != nil {
 			version = APIVERSION
 			version = APIVERSION
 		}
 		}
-		if srv.enableCors {
+		if srv.runtime.config.EnableCors {
 			writeCorsHeaders(w, r)
 			writeCorsHeaders(w, r)
 		}
 		}
 
 

+ 9 - 17
api_params.go

@@ -1,7 +1,5 @@
 package docker
 package docker
 
 
-import "encoding/json"
-
 type APIHistory struct {
 type APIHistory struct {
 	ID        string   `json:"Id"`
 	ID        string   `json:"Id"`
 	Tags      []string `json:",omitempty"`
 	Tags      []string `json:",omitempty"`
@@ -52,17 +50,18 @@ type APIContainers struct {
 	Ports      []APIPort
 	Ports      []APIPort
 	SizeRw     int64
 	SizeRw     int64
 	SizeRootFs int64
 	SizeRootFs int64
+	Names      []string
 }
 }
 
 
 func (self *APIContainers) ToLegacy() APIContainersOld {
 func (self *APIContainers) ToLegacy() APIContainersOld {
 	return APIContainersOld{
 	return APIContainersOld{
-		ID: self.ID,
-		Image: self.Image,
-		Command: self.Command,
-		Created: self.Created,
-		Status: self.Status,
-		Ports: displayablePorts(self.Ports),
-		SizeRw: self.SizeRw,
+		ID:         self.ID,
+		Image:      self.Image,
+		Command:    self.Command,
+		Created:    self.Created,
+		Status:     self.Status,
+		Ports:      displayablePorts(self.Ports),
+		SizeRw:     self.SizeRw,
 		SizeRootFs: self.SizeRootFs,
 		SizeRootFs: self.SizeRootFs,
 	}
 	}
 }
 }
@@ -96,14 +95,7 @@ type APIPort struct {
 	PrivatePort int64
 	PrivatePort int64
 	PublicPort  int64
 	PublicPort  int64
 	Type        string
 	Type        string
-}
-
-func (port *APIPort) MarshalJSON() ([]byte, error) {
-	return json.Marshal(map[string]interface{}{
-		"PrivatePort": port.PrivatePort,
-		"PublicPort":  port.PublicPort,
-		"Type":        port.Type,
-	})
+	IP          string
 }
 }
 
 
 type APIVersion struct {
 type APIVersion struct {

+ 42 - 27
api_test.go

@@ -112,6 +112,7 @@ func TestGetInfo(t *testing.T) {
 
 
 func TestGetEvents(t *testing.T) {
 func TestGetEvents(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
+	defer nuke(runtime)
 	srv := &Server{
 	srv := &Server{
 		runtime:   runtime,
 		runtime:   runtime,
 		events:    make([]utils.JSONMessage, 0, 64),
 		events:    make([]utils.JSONMessage, 0, 64),
@@ -346,10 +347,12 @@ func TestGetContainersJSON(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(&Config{
+	beginLen := runtime.containers.Len()
+
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"echo", "test"},
 		Cmd:   []string{"echo", "test"},
-	})
+	}, "")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -368,8 +371,8 @@ func TestGetContainersJSON(t *testing.T) {
 	if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
 	if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if len(containers) != 1 {
-		t.Fatalf("Expected %d container, %d found", 1, len(containers))
+	if len(containers) != beginLen+1 {
+		t.Fatalf("Expected %d container, %d found (started with: %d)", beginLen+1, len(containers), beginLen)
 	}
 	}
 	if containers[0].ID != container.ID {
 	if containers[0].ID != container.ID {
 		t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.ID, containers[0].ID)
 		t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.ID, containers[0].ID)
@@ -383,11 +386,12 @@ func TestGetContainersExport(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"touch", "/test"},
 			Cmd:   []string{"touch", "/test"},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -433,11 +437,12 @@ func TestGetContainersChanges(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -471,20 +476,18 @@ func TestGetContainersChanges(t *testing.T) {
 
 
 func TestGetContainersTop(t *testing.T) {
 func TestGetContainersTop(t *testing.T) {
 	t.Skip("Fixme. Skipping test for now. Reported error when testing using dind: 'api_test.go:527: Expected 2 processes, found 0.'")
 	t.Skip("Fixme. Skipping test for now. Reported error when testing using dind: 'api_test.go:527: Expected 2 processes, found 0.'")
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/sh", "-c", "cat"},
 			Cmd:       []string{"/bin/sh", "-c", "cat"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -561,11 +564,12 @@ func TestGetContainersByName(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "test"},
 			Cmd:   []string{"echo", "test"},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -592,11 +596,12 @@ func TestPostCommit(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"touch", "/test"},
 			Cmd:   []string{"touch", "/test"},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -686,12 +691,13 @@ func TestPostContainersKill(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -728,12 +734,13 @@ func TestPostContainersRestart(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Image:     GetTestImage(runtime).ID,
-			Cmd:       []string{"/bin/cat"},
+			Cmd:       []string{"/bin/top"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -782,12 +789,13 @@ func TestPostContainersStart(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -834,12 +842,13 @@ func TestPostContainersStop(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Image:     GetTestImage(runtime).ID,
-			Cmd:       []string{"/bin/cat"},
+			Cmd:       []string{"/bin/top"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -881,12 +890,13 @@ func TestPostContainersWait(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/sleep", "1"},
 			Cmd:       []string{"/bin/sleep", "1"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -923,12 +933,13 @@ func TestPostContainersAttach(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1012,12 +1023,13 @@ func TestPostContainersAttachStderr(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:     GetTestImage(runtime).ID,
 			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/sh", "-c", "/bin/cat >&2"},
 			Cmd:       []string{"/bin/sh", "-c", "/bin/cat >&2"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1104,10 +1116,10 @@ func TestDeleteContainers(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"touch", "/test"},
 		Cmd:   []string{"touch", "/test"},
-	})
+	}, "")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1142,7 +1154,8 @@ func TestOptionsRoute(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	srv := &Server{runtime: runtime, enableCors: true}
+	runtime.config.EnableCors = true
+	srv := &Server{runtime: runtime}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 	router, err := createRouter(srv, false)
 	router, err := createRouter(srv, false)
@@ -1165,7 +1178,8 @@ func TestGetEnabledCors(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	srv := &Server{runtime: runtime, enableCors: true}
+	runtime.config.EnableCors = true
+	srv := &Server{runtime: runtime}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 
 
@@ -1292,11 +1306,12 @@ func TestPostContainersCopy(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"touch", "/test.txt"},
 			Cmd:   []string{"touch", "/test.txt"},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)

+ 4 - 1
auth/auth_test.go

@@ -76,7 +76,7 @@ func TestCreateAccount(t *testing.T) {
 }
 }
 
 
 func setupTempConfigFile() (*ConfigFile, error) {
 func setupTempConfigFile() (*ConfigFile, error) {
-	root, err := ioutil.TempDir("", "docker-test")
+	root, err := ioutil.TempDir("", "docker-test-auth")
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -101,6 +101,7 @@ func TestSameAuthDataPostSave(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+	defer os.RemoveAll(configFile.rootPath)
 
 
 	err = SaveConfig(configFile)
 	err = SaveConfig(configFile)
 	if err != nil {
 	if err != nil {
@@ -127,6 +128,7 @@ func TestResolveAuthConfigIndexServer(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+	defer os.RemoveAll(configFile.rootPath)
 
 
 	for _, registry := range []string{"", IndexServerAddress()} {
 	for _, registry := range []string{"", IndexServerAddress()} {
 		resolved := configFile.ResolveAuthConfig(registry)
 		resolved := configFile.ResolveAuthConfig(registry)
@@ -141,6 +143,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+	defer os.RemoveAll(configFile.rootPath)
 
 
 	registryAuth := AuthConfig{
 	registryAuth := AuthConfig{
 		Username: "foo-user",
 		Username: "foo-user",

+ 16 - 6
buildfile.go

@@ -187,6 +187,9 @@ func (b *buildFile) CmdCmd(args string) error {
 }
 }
 
 
 func (b *buildFile) CmdExpose(args string) error {
 func (b *buildFile) CmdExpose(args string) error {
+	if strings.Contains(args, ":") {
+		return fmt.Errorf("EXPOSE cannot be used to bind to a host ip or port")
+	}
 	ports := strings.Split(args, " ")
 	ports := strings.Split(args, " ")
 	b.config.PortSpecs = append(ports, b.config.PortSpecs...)
 	b.config.PortSpecs = append(ports, b.config.PortSpecs...)
 	return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
 	return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
@@ -332,7 +335,7 @@ func (b *buildFile) CmdAdd(args string) error {
 
 
 	b.config.Image = b.image
 	b.config.Image = b.image
 	// Create the container and start it
 	// Create the container and start it
-	container, err := b.runtime.Create(b.config)
+	container, _, err := b.runtime.Create(b.config, "")
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -367,7 +370,7 @@ func (b *buildFile) run() (string, error) {
 	b.config.Image = b.image
 	b.config.Image = b.image
 
 
 	// Create the container and start it
 	// Create the container and start it
-	c, err := b.runtime.Create(b.config)
+	c, _, err := b.runtime.Create(b.config, "")
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
@@ -378,15 +381,22 @@ func (b *buildFile) run() (string, error) {
 	c.Path = b.config.Cmd[0]
 	c.Path = b.config.Cmd[0]
 	c.Args = b.config.Cmd[1:]
 	c.Args = b.config.Cmd[1:]
 
 
+	var errCh chan error
+
+	if b.verbose {
+		errCh = utils.Go(func() error {
+			return <-c.Attach(nil, nil, b.out, b.out)
+		})
+	}
+
 	//start the container
 	//start the container
 	hostConfig := &HostConfig{}
 	hostConfig := &HostConfig{}
 	if err := c.Start(hostConfig); err != nil {
 	if err := c.Start(hostConfig); err != nil {
 		return "", err
 		return "", err
 	}
 	}
 
 
-	if b.verbose {
-		err = <-c.Attach(nil, nil, b.out, b.out)
-		if err != nil {
+	if errCh != nil {
+		if err := <-errCh; err != nil {
 			return "", err
 			return "", err
 		}
 		}
 	}
 	}
@@ -423,7 +433,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
 			}
 			}
 		}
 		}
 
 
-		container, err := b.runtime.Create(b.config)
+		container, _, err := b.runtime.Create(b.config, "")
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}

+ 6 - 24
buildfile_test.go

@@ -229,10 +229,7 @@ func TestBuild(t *testing.T) {
 
 
 func buildImage(context testContextTemplate, t *testing.T, srv *Server, useCache bool) *Image {
 func buildImage(context testContextTemplate, t *testing.T, srv *Server, useCache bool) *Image {
 	if srv == nil {
 	if srv == nil {
-		runtime, err := newTestRuntime()
-		if err != nil {
-			t.Fatal(err)
-		}
+		runtime := mkRuntime(t)
 		defer nuke(runtime)
 		defer nuke(runtime)
 
 
 		srv = &Server{
 		srv = &Server{
@@ -370,10 +367,7 @@ func TestBuildEntrypoint(t *testing.T) {
 // testing #1405 - config.Cmd does not get cleaned up if
 // testing #1405 - config.Cmd does not get cleaned up if
 // utilizing cache
 // utilizing cache
 func TestBuildEntrypointRunCleanup(t *testing.T) {
 func TestBuildEntrypointRunCleanup(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
 	srv := &Server{
 	srv := &Server{
@@ -402,10 +396,7 @@ func TestBuildEntrypointRunCleanup(t *testing.T) {
 }
 }
 
 
 func TestBuildImageWithCache(t *testing.T) {
 func TestBuildImageWithCache(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
 	srv := &Server{
 	srv := &Server{
@@ -433,10 +424,7 @@ func TestBuildImageWithCache(t *testing.T) {
 }
 }
 
 
 func TestBuildImageWithoutCache(t *testing.T) {
 func TestBuildImageWithoutCache(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
 	srv := &Server{
 	srv := &Server{
@@ -464,10 +452,7 @@ func TestBuildImageWithoutCache(t *testing.T) {
 }
 }
 
 
 func TestForbiddenContextPath(t *testing.T) {
 func TestForbiddenContextPath(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
 	srv := &Server{
 	srv := &Server{
@@ -513,10 +498,7 @@ func TestForbiddenContextPath(t *testing.T) {
 }
 }
 
 
 func TestBuildADDFileNotFound(t *testing.T) {
 func TestBuildADDFileNotFound(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
 	srv := &Server{
 	srv := &Server{

+ 253 - 113
commands.go

@@ -23,6 +23,7 @@ import (
 	"os/signal"
 	"os/signal"
 	"path/filepath"
 	"path/filepath"
 	"reflect"
 	"reflect"
+	"regexp"
 	"runtime"
 	"runtime"
 	"sort"
 	"sort"
 	"strconv"
 	"strconv"
@@ -41,9 +42,13 @@ var (
 	ErrConnectionRefused = errors.New("Can't connect to docker daemon. Is 'docker -d' running on this host?")
 	ErrConnectionRefused = errors.New("Can't connect to docker daemon. Is 'docker -d' running on this host?")
 )
 )
 
 
-func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
+func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) {
 	methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
 	methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
-	return reflect.TypeOf(cli).MethodByName(methodName)
+	method := reflect.ValueOf(cli).MethodByName(methodName)
+	if !method.IsValid() {
+		return nil, false
+	}
+	return method.Interface().(func(...string) error), true
 }
 }
 
 
 func ParseCommands(proto, addr string, args ...string) error {
 func ParseCommands(proto, addr string, args ...string) error {
@@ -55,14 +60,7 @@ func ParseCommands(proto, addr string, args ...string) error {
 			fmt.Println("Error: Command not found:", args[0])
 			fmt.Println("Error: Command not found:", args[0])
 			return cli.CmdHelp(args[1:]...)
 			return cli.CmdHelp(args[1:]...)
 		}
 		}
-		ret := method.Func.CallSlice([]reflect.Value{
-			reflect.ValueOf(cli),
-			reflect.ValueOf(args[1:]),
-		})[0].Interface()
-		if ret == nil {
-			return nil
-		}
-		return ret.(error)
+		return method(args[1:]...)
 	}
 	}
 	return cli.CmdHelp(args...)
 	return cli.CmdHelp(args...)
 }
 }
@@ -73,10 +71,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 		if !exists {
 		if !exists {
 			fmt.Fprintf(cli.err, "Error: Command not found: %s\n", args[0])
 			fmt.Fprintf(cli.err, "Error: Command not found: %s\n", args[0])
 		} else {
 		} else {
-			method.Func.CallSlice([]reflect.Value{
-				reflect.ValueOf(cli),
-				reflect.ValueOf([]string{"--help"}),
-			})[0].Interface()
+			method("--help")
 			return nil
 			return nil
 		}
 		}
 	}
 	}
@@ -99,7 +94,6 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 		{"login", "Register or Login to the docker registry server"},
 		{"login", "Register or Login to the docker registry server"},
 		{"logs", "Fetch the logs of a container"},
 		{"logs", "Fetch the logs of a container"},
 		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
 		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
-		{"top", "Lookup the running processes of a container"},
 		{"ps", "List containers"},
 		{"ps", "List containers"},
 		{"pull", "Pull an image or a repository from the docker registry server"},
 		{"pull", "Pull an image or a repository from the docker registry server"},
 		{"push", "Push an image or a repository to the docker registry server"},
 		{"push", "Push an image or a repository to the docker registry server"},
@@ -111,6 +105,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 		{"start", "Start a stopped container"},
 		{"start", "Start a stopped container"},
 		{"stop", "Stop a running container"},
 		{"stop", "Stop a running container"},
 		{"tag", "Tag an image into a repository"},
 		{"tag", "Tag an image into a repository"},
+		{"top", "Lookup the running processes of a container"},
 		{"version", "Show the docker version information"},
 		{"version", "Show the docker version information"},
 		{"wait", "Block until a container stops, then print its exit code"},
 		{"wait", "Block until a container stops, then print its exit code"},
 	} {
 	} {
@@ -545,8 +540,23 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
 	return nil
 	return nil
 }
 }
 
 
+func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
+	sigc := make(chan os.Signal, 1)
+	utils.CatchAll(sigc)
+	go func() {
+		for s := range sigc {
+			if _, _, err := cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%d", cid, s), nil); err != nil {
+				utils.Debugf("Error sending signal: %s", err)
+			}
+		}
+	}()
+	return sigc
+}
+
 func (cli *DockerCli) CmdStart(args ...string) error {
 func (cli *DockerCli) CmdStart(args ...string) error {
 	cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
 	cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
+	attach := cmd.Bool("a", false, "Attach container's stdout/stderr and forward all signals to the process")
+	openStdin := cmd.Bool("i", false, "Attach container's stdin")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
 	}
 	}
@@ -555,17 +565,75 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 		return nil
 		return nil
 	}
 	}
 
 
+	var cErr chan error
+	if *attach || *openStdin {
+		if cmd.NArg() > 1 {
+			return fmt.Errorf("Impossible to start and attach multiple containers at once.")
+		}
+
+		body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
+		if err != nil {
+			return err
+		}
+
+		container := &Container{}
+		err = json.Unmarshal(body, container)
+		if err != nil {
+			return err
+		}
+
+		if !container.Config.Tty {
+			sigc := cli.forwardAllSignals(cmd.Arg(0))
+			defer utils.StopCatch(sigc)
+		}
+
+		if container.Config.Tty && cli.isTerminal {
+			if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
+				return err
+			}
+		}
+
+		var in io.ReadCloser
+
+		v := url.Values{}
+		v.Set("stream", "1")
+		if *openStdin && container.Config.OpenStdin {
+			v.Set("stdin", "1")
+			in = cli.in
+		}
+		v.Set("stdout", "1")
+		v.Set("stderr", "1")
+
+		cErr = utils.Go(func() error {
+			return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, in, cli.out, cli.err, nil)
+		})
+	}
+
 	var encounteredError error
 	var encounteredError error
-	for _, name := range args {
+	for _, name := range cmd.Args() {
 		_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
 		_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
 		if err != nil {
 		if err != nil {
-			fmt.Fprintf(cli.err, "%s\n", err)
-			encounteredError = fmt.Errorf("Error: failed to start one or more containers")
+			if !*attach || !*openStdin {
+				fmt.Fprintf(cli.err, "%s\n", err)
+				encounteredError = fmt.Errorf("Error: failed to start one or more containers")
+			}
 		} else {
 		} else {
-			fmt.Fprintf(cli.out, "%s\n", name)
+			if !*attach || !*openStdin {
+				fmt.Fprintf(cli.out, "%s\n", name)
+			}
+		}
+	}
+	if encounteredError != nil {
+		if *openStdin || *attach {
+			cli.in.Close()
+			<-cErr
 		}
 		}
+		return encounteredError
 	}
 	}
-	return encounteredError
+	if *openStdin || *attach {
+		return <-cErr
+	}
+	return nil
 }
 }
 
 
 func (cli *DockerCli) CmdInspect(args ...string) error {
 func (cli *DockerCli) CmdInspect(args ...string) error {
@@ -577,30 +645,39 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 		cmd.Usage()
 		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
-	fmt.Fprintf(cli.out, "[")
-	for i, name := range args {
-		if i > 0 {
-			fmt.Fprintf(cli.out, ",")
-		}
+
+	indented := new(bytes.Buffer)
+	status := 0
+
+	for _, name := range args {
 		obj, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
 		obj, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
 		if err != nil {
 		if err != nil {
 			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
 			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
 			if err != nil {
 			if err != nil {
 				fmt.Fprintf(cli.err, "No such image or container: %s\n", name)
 				fmt.Fprintf(cli.err, "No such image or container: %s\n", name)
+				status = 1
 				continue
 				continue
 			}
 			}
 		}
 		}
 
 
-		indented := new(bytes.Buffer)
 		if err = json.Indent(indented, obj, "", "    "); err != nil {
 		if err = json.Indent(indented, obj, "", "    "); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
+			status = 1
 			continue
 			continue
 		}
 		}
-		if _, err := io.Copy(cli.out, indented); err != nil {
-			fmt.Fprintf(cli.err, "%s\n", err)
-		}
+		indented.WriteString(",")
+	}
+	// Remove trailling ','
+	indented.Truncate(indented.Len() - 1)
+
+	fmt.Fprintf(cli.out, "[")
+	if _, err := io.Copy(cli.out, indented); err != nil {
+		return err
 	}
 	}
 	fmt.Fprintf(cli.out, "]")
 	fmt.Fprintf(cli.out, "]")
+	if status != 0 {
+		return &utils.StatusError{Status: status}
+	}
 	return nil
 	return nil
 }
 }
 
 
@@ -647,11 +724,11 @@ func (cli *DockerCli) CmdPort(args ...string) error {
 	}
 	}
 
 
 	port := cmd.Arg(1)
 	port := cmd.Arg(1)
-	proto := "Tcp"
+	proto := "tcp"
 	parts := strings.SplitN(port, "/", 2)
 	parts := strings.SplitN(port, "/", 2)
 	if len(parts) == 2 && len(parts[1]) != 0 {
 	if len(parts) == 2 && len(parts[1]) != 0 {
 		port = parts[0]
 		port = parts[0]
-		proto = strings.ToUpper(parts[1][:1]) + strings.ToLower(parts[1][1:])
+		proto = parts[1]
 	}
 	}
 	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
 	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
 	if err != nil {
 	if err != nil {
@@ -663,8 +740,14 @@ func (cli *DockerCli) CmdPort(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	if frontend, exists := out.NetworkSettings.PortMapping[proto][port]; exists {
-		fmt.Fprintf(cli.out, "%s\n", frontend)
+	if frontends, exists := out.NetworkSettings.Ports[Port(port+"/"+proto)]; exists {
+		if frontends == nil {
+			fmt.Fprintf(cli.out, "%s\n", port)
+		} else {
+			for _, frontend := range frontends {
+				fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
+			}
+		}
 	} else {
 	} else {
 		return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0))
 		return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0))
 	}
 	}
@@ -740,6 +823,8 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
 func (cli *DockerCli) CmdRm(args ...string) error {
 func (cli *DockerCli) CmdRm(args ...string) error {
 	cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
 	cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
 	v := cmd.Bool("v", false, "Remove the volumes associated to the container")
 	v := cmd.Bool("v", false, "Remove the volumes associated to the container")
+	link := cmd.Bool("link", false, "Remove the specified link and not the underlying container")
+
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
 	}
 	}
@@ -751,6 +836,9 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 	if *v {
 	if *v {
 		val.Set("v", "1")
 		val.Set("v", "1")
 	}
 	}
+	if *link {
+		val.Set("link", "1")
+	}
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
 		_, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
 		_, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
 		if err != nil {
 		if err != nil {
@@ -985,25 +1073,19 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 				out.Tag = "<none>"
 				out.Tag = "<none>"
 			}
 			}
 
 
+			if !*noTrunc {
+				out.ID = utils.TruncateID(out.ID)
+			}
+
 			if !*quiet {
 			if !*quiet {
-				fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag)
-				if *noTrunc {
-					fmt.Fprintf(w, "%s\t", out.ID)
-				} else {
-					fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID))
-				}
-				fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))))
+				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t", out.Repository, out.Tag, out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))))
 				if out.VirtualSize > 0 {
 				if out.VirtualSize > 0 {
 					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.Size), utils.HumanSize(out.VirtualSize))
 					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.Size), utils.HumanSize(out.VirtualSize))
 				} else {
 				} else {
 					fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size))
 					fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size))
 				}
 				}
 			} else {
 			} else {
-				if *noTrunc {
-					fmt.Fprintln(w, out.ID)
-				} else {
-					fmt.Fprintln(w, utils.TruncateID(out.ID))
-				}
+				fmt.Fprintln(w, out.ID)
 			}
 			}
 		}
 		}
 
 
@@ -1017,10 +1099,10 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 func displayablePorts(ports []APIPort) string {
 func displayablePorts(ports []APIPort) string {
 	result := []string{}
 	result := []string{}
 	for _, port := range ports {
 	for _, port := range ports {
-		if port.Type == "tcp" {
-			result = append(result, fmt.Sprintf("%d->%d", port.PublicPort, port.PrivatePort))
+		if port.IP == "" {
+			result = append(result, fmt.Sprintf("%d/%s", port.PublicPort, port.Type))
 		} else {
 		} else {
-			result = append(result, fmt.Sprintf("%d->%d/%s", port.PublicPort, port.PrivatePort, port.Type))
+			result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type))
 		}
 		}
 	}
 	}
 	sort.Strings(result)
 	sort.Strings(result)
@@ -1073,7 +1155,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 	}
 	}
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	if !*quiet {
 	if !*quiet {
-		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS")
+		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
 		if *size {
 		if *size {
 			fmt.Fprintln(w, "\tSIZE")
 			fmt.Fprintln(w, "\tSIZE")
 		} else {
 		} else {
@@ -1082,12 +1164,20 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 	}
 	}
 
 
 	for _, out := range outs {
 	for _, out := range outs {
+		if !*noTrunc {
+			out.ID = utils.TruncateID(out.ID)
+		}
+
+		// Remove the leading / from the names
+		for i := 0; i < len(out.Names); i++ {
+			out.Names[i] = out.Names[i][1:]
+		}
+
 		if !*quiet {
 		if !*quiet {
-			if *noTrunc {
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports))
-			} else {
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports))
+			if !*noTrunc {
+				out.Command = utils.Trunc(out.Command, 20)
 			}
 			}
+			fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\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), strings.Join(out.Names, ","))
 			if *size {
 			if *size {
 				if out.SizeRootFs > 0 {
 				if out.SizeRootFs > 0 {
 					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs))
 					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs))
@@ -1098,11 +1188,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 				fmt.Fprint(w, "\n")
 				fmt.Fprint(w, "\n")
 			}
 			}
 		} else {
 		} else {
-			if *noTrunc {
-				fmt.Fprintln(w, out.ID)
-			} else {
-				fmt.Fprintln(w, utils.TruncateID(out.ID))
-			}
+			fmt.Fprintln(w, out.ID)
 		}
 		}
 	}
 	}
 
 
@@ -1229,15 +1315,18 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
 		cmd.Usage()
 		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
+	name := cmd.Arg(0)
 
 
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err); err != nil {
+	if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err, nil); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
 func (cli *DockerCli) CmdAttach(args ...string) error {
 func (cli *DockerCli) CmdAttach(args ...string) error {
-	cmd := Subcmd("attach", "CONTAINER", "Attach to a running container")
+	cmd := Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container")
+	noStdin := cmd.Bool("nostdin", false, "Do not attach stdin")
+	proxy := cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
 	}
 	}
@@ -1245,8 +1334,8 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		cmd.Usage()
 		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
-
-	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
+	name := cmd.Arg(0)
+	body, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -1261,19 +1350,29 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		return fmt.Errorf("Impossible to attach to a stopped container, start it first")
 		return fmt.Errorf("Impossible to attach to a stopped container, start it first")
 	}
 	}
 
 
-	if container.Config.Tty {
+	if container.Config.Tty && cli.isTerminal {
 		if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
 		if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
-			utils.Debugf("Error monitoring tty size: %s", err)
+			utils.Debugf("Error monitoring TTY size: %s", err)
 		}
 		}
 	}
 	}
 
 
+	var in io.ReadCloser
+
 	v := url.Values{}
 	v := url.Values{}
 	v.Set("stream", "1")
 	v.Set("stream", "1")
-	v.Set("stdin", "1")
+	if !*noStdin && container.Config.OpenStdin {
+		v.Set("stdin", "1")
+		in = cli.in
+	}
 	v.Set("stdout", "1")
 	v.Set("stdout", "1")
 	v.Set("stderr", "1")
 	v.Set("stderr", "1")
 
 
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, cli.in, cli.out, cli.err); err != nil {
+	if *proxy && !container.Config.Tty {
+		sigc := cli.forwardAllSignals(cmd.Arg(0))
+		defer utils.StopCatch(sigc)
+	}
+
+	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, in, cli.out, cli.err, nil); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -1326,18 +1425,6 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 // Ports type - Used to parse multiple -p flags
 // Ports type - Used to parse multiple -p flags
 type ports []int
 type ports []int
 
 
-// ListOpts type
-type ListOpts []string
-
-func (opts *ListOpts) String() string {
-	return fmt.Sprint(*opts)
-}
-
-func (opts *ListOpts) Set(value string) error {
-	*opts = append(*opts, value)
-	return nil
-}
-
 // AttachOpts stores arguments to 'docker run -a', eg. which streams to attach to
 // AttachOpts stores arguments to 'docker run -a', eg. which streams to attach to
 type AttachOpts map[string]bool
 type AttachOpts map[string]bool
 
 
@@ -1436,6 +1523,13 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	flRm := cmd.Lookup("rm")
 	flRm := cmd.Lookup("rm")
 	autoRemove, _ := strconv.ParseBool(flRm.Value.String())
 	autoRemove, _ := strconv.ParseBool(flRm.Value.String())
 
 
+	flSigProxy := cmd.Lookup("sig-proxy")
+	sigProxy, _ := strconv.ParseBool(flSigProxy.Value.String())
+	flName := cmd.Lookup("name")
+	if config.Tty {
+		sigProxy = false
+	}
+
 	var containerIDFile *os.File
 	var containerIDFile *os.File
 	if len(hostConfig.ContainerIDFile) > 0 {
 	if len(hostConfig.ContainerIDFile) > 0 {
 		if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil {
 		if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil {
@@ -1447,9 +1541,14 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		}
 		}
 		defer containerIDFile.Close()
 		defer containerIDFile.Close()
 	}
 	}
+	containerValues := url.Values{}
+	name := flName.Value.String()
+	if name != "" {
+		containerValues.Set("name", name)
+	}
 
 
 	//create the container
 	//create the container
-	body, statusCode, err := cli.call("POST", "/containers/create", config)
+	body, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config)
 	//if image not found try to pull it
 	//if image not found try to pull it
 	if statusCode == 404 {
 	if statusCode == 404 {
 		_, tag := utils.ParseRepositoryTag(config.Image)
 		_, tag := utils.ParseRepositoryTag(config.Image)
@@ -1490,7 +1589,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		body, _, err = cli.call("POST", "/containers/create", config)
+		body, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -1514,12 +1613,15 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		}
 		}
 	}
 	}
 
 
-	//start the container
-	if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil {
-		return err
+	if sigProxy {
+		sigc := cli.forwardAllSignals(runResult.ID)
+		defer utils.StopCatch(sigc)
 	}
 	}
 
 
-	var wait chan struct{}
+	var (
+		wait  chan struct{}
+		errCh chan error
+	)
 
 
 	if !config.AttachStdout && !config.AttachStderr {
 	if !config.AttachStdout && !config.AttachStderr {
 		// Make this asynchrone in order to let the client write to stdin before having to read the ID
 		// Make this asynchrone in order to let the client write to stdin before having to read the ID
@@ -1530,20 +1632,18 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		}()
 		}()
 	}
 	}
 
 
+	hijacked := make(chan bool)
+
 	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
 	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
-		if config.Tty {
-			if err := cli.monitorTtySize(runResult.ID); err != nil {
-				utils.Errorf("Error monitoring TTY size: %s\n", err)
-			}
-		}
 
 
 		v := url.Values{}
 		v := url.Values{}
-		v.Set("logs", "1")
 		v.Set("stream", "1")
 		v.Set("stream", "1")
 		var out, stderr io.Writer
 		var out, stderr io.Writer
+		var in io.ReadCloser
 
 
 		if config.AttachStdin {
 		if config.AttachStdin {
 			v.Set("stdin", "1")
 			v.Set("stdin", "1")
+			in = cli.in
 		}
 		}
 		if config.AttachStdout {
 		if config.AttachStdout {
 			v.Set("stdout", "1")
 			v.Set("stdout", "1")
@@ -1558,18 +1658,36 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 			}
 			}
 		}
 		}
 
 
-		signals := make(chan os.Signal, 1)
-		signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
-		go func() {
-			for sig := range signals {
-				fmt.Printf("\nReceived signal: %s; cleaning up\n", sig)
-				if err := cli.CmdStop("-t", "4", runResult.ID); err != nil {
-					fmt.Printf("failed to stop container: %v", err)
-				}
-			}
-		}()
+		errCh = utils.Go(func() error {
+			return cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked)
+		})
+	} else {
+		close(hijacked)
+	}
 
 
-		if err := cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, cli.in, out, stderr); err != nil {
+	// Acknowledge the hijack before starting
+	select {
+	case <-hijacked:
+	case err := <-errCh:
+		if err != nil {
+			utils.Debugf("Error hijack: %s", err)
+			return err
+		}
+	}
+
+	//start the container
+	if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil {
+		return err
+	}
+
+	if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminal {
+		if err := cli.monitorTtySize(runResult.ID); err != nil {
+			utils.Errorf("Error monitoring TTY size: %s\n", err)
+		}
+	}
+
+	if errCh != nil {
+		if err := <-errCh; err != nil {
 			utils.Debugf("Error hijack: %s", err)
 			utils.Debugf("Error hijack: %s", err)
 			return err
 			return err
 		}
 		}
@@ -1579,13 +1697,19 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		// Detached mode
 		// Detached mode
 		<-wait
 		<-wait
 	} else {
 	} else {
-		status, err := getExitCode(cli, runResult.ID)
+		running, status, err := getExitCode(cli, runResult.ID)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 		if autoRemove {
 		if autoRemove {
-			_, _, err = cli.call("DELETE", "/containers/"+runResult.ID, nil)
-			if err != nil {
+			if running {
+				return fmt.Errorf("Impossible to auto-remove a detached container")
+			}
+			// Wait for the process to
+			if _, _, err := cli.call("POST", "/containers/"+runResult.ID+"/wait", nil); err != nil {
+				return err
+			}
+			if _, _, err := cli.call("DELETE", "/containers/"+runResult.ID, nil); err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}
@@ -1642,6 +1766,10 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
 		params = bytes.NewBuffer(buf)
 		params = bytes.NewBuffer(buf)
 	}
 	}
 
 
+	// fixme: refactor client to support redirect
+	re := regexp.MustCompile("/+")
+	path = re.ReplaceAllString(path, "/")
+
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params)
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params)
 	if err != nil {
 	if err != nil {
 		return nil, -1, err
 		return nil, -1, err
@@ -1670,6 +1798,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
 		return nil, -1, err
 		return nil, -1, err
 	}
 	}
 	defer resp.Body.Close()
 	defer resp.Body.Close()
+
 	body, err := ioutil.ReadAll(resp.Body)
 	body, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
 	if err != nil {
 		return nil, -1, err
 		return nil, -1, err
@@ -1687,6 +1816,11 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
 	if (method == "POST" || method == "PUT") && in == nil {
 	if (method == "POST" || method == "PUT") && in == nil {
 		in = bytes.NewReader([]byte{})
 		in = bytes.NewReader([]byte{})
 	}
 	}
+
+	// fixme: refactor client to support redirect
+	re := regexp.MustCompile("/+")
+	path = re.ReplaceAllString(path, "/")
+
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in)
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -1742,7 +1876,10 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
 	return nil
 	return nil
 }
 }
 
 
-func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer) error {
+func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan bool) error {
+	// fixme: refactor client to support redirect
+	re := regexp.MustCompile("/+")
+	path = re.ReplaceAllString(path, "/")
 
 
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
 	if err != nil {
 	if err != nil {
@@ -1768,6 +1905,10 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
 	rwc, br := clientconn.Hijack()
 	rwc, br := clientconn.Hijack()
 	defer rwc.Close()
 	defer rwc.Close()
 
 
+	if started != nil {
+		started <- true
+	}
+
 	var receiveStdout chan error
 	var receiveStdout chan error
 
 
 	if stdout != nil {
 	if stdout != nil {
@@ -1854,9 +1995,6 @@ func (cli *DockerCli) resizeTty(id string) {
 }
 }
 
 
 func (cli *DockerCli) monitorTtySize(id string) error {
 func (cli *DockerCli) monitorTtySize(id string) error {
-	if !cli.isTerminal {
-		return fmt.Errorf("Impossible to monitor size on non-tty")
-	}
 	cli.resizeTty(id)
 	cli.resizeTty(id)
 
 
 	sigchan := make(chan os.Signal, 1)
 	sigchan := make(chan os.Signal, 1)
@@ -1904,20 +2042,22 @@ func waitForExit(cli *DockerCli, containerId string) (int, error) {
 	return out.StatusCode, nil
 	return out.StatusCode, nil
 }
 }
 
 
-func getExitCode(cli *DockerCli, containerId string) (int, error) {
+// getExitCode perform an inspect on the container. It returns
+// the running state and the exit code.
+func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
 	body, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil)
 	body, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil)
 	if err != nil {
 	if err != nil {
 		// If we can't connect, then the daemon probably died.
 		// If we can't connect, then the daemon probably died.
 		if err != ErrConnectionRefused {
 		if err != ErrConnectionRefused {
-			return -1, err
+			return false, -1, err
 		}
 		}
-		return -1, nil
+		return false, -1, nil
 	}
 	}
 	c := &Container{}
 	c := &Container{}
 	if err := json.Unmarshal(body, c); err != nil {
 	if err := json.Unmarshal(body, c); err != nil {
-		return -1, err
+		return false, -1, err
 	}
 	}
-	return c.State.ExitCode, nil
+	return c.State.Running, c.State.ExitCode, nil
 }
 }
 
 
 func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
 func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {

+ 81 - 16
commands_test.go

@@ -84,10 +84,24 @@ func TestRunHostname(t *testing.T) {
 		}
 		}
 	})
 	})
 
 
-	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
+	container := globalRuntime.List()[0]
+
+	setTimeout(t, "CmdRun timed out", 10*time.Second, func() {
 		<-c
 		<-c
+
+		go func() {
+			cli.CmdWait(container.ID)
+		}()
+
+		if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
+			t.Fatal(err)
+		}
 	})
 	})
 
 
+	// Cleanup pipes
+	if err := closeWrap(stdout, stdoutPipe); err != nil {
+		t.Fatal(err)
+	}
 }
 }
 
 
 // TestRunWorkdir checks that 'docker run -w' correctly sets a custom working directory
 // TestRunWorkdir checks that 'docker run -w' correctly sets a custom working directory
@@ -115,10 +129,24 @@ func TestRunWorkdir(t *testing.T) {
 		}
 		}
 	})
 	})
 
 
-	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
+	container := globalRuntime.List()[0]
+
+	setTimeout(t, "CmdRun timed out", 10*time.Second, func() {
 		<-c
 		<-c
+
+		go func() {
+			cli.CmdWait(container.ID)
+		}()
+
+		if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
+			t.Fatal(err)
+		}
 	})
 	})
 
 
+	// Cleanup pipes
+	if err := closeWrap(stdout, stdoutPipe); err != nil {
+		t.Fatal(err)
+	}
 }
 }
 
 
 // TestRunWorkdirExists checks that 'docker run -w' correctly sets a custom working directory, even if it exists
 // TestRunWorkdirExists checks that 'docker run -w' correctly sets a custom working directory, even if it exists
@@ -146,10 +174,24 @@ func TestRunWorkdirExists(t *testing.T) {
 		}
 		}
 	})
 	})
 
 
+	container := globalRuntime.List()[0]
+
 	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
 	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
 		<-c
 		<-c
+
+		go func() {
+			cli.CmdWait(container.ID)
+		}()
+
+		if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
+			t.Fatal(err)
+		}
 	})
 	})
 
 
+	// Cleanup pipes
+	if err := closeWrap(stdout, stdoutPipe); err != nil {
+		t.Fatal(err)
+	}
 }
 }
 
 
 func TestRunExit(t *testing.T) {
 func TestRunExit(t *testing.T) {
@@ -262,7 +304,7 @@ func TestRunDisconnectTty(t *testing.T) {
 		// We're simulating a disconnect so the return value doesn't matter. What matters is the
 		// We're simulating a disconnect so the return value doesn't matter. What matters is the
 		// fact that CmdRun returns.
 		// fact that CmdRun returns.
 		if err := cli.CmdRun("-i", "-t", unitTestImageID, "/bin/cat"); err != nil {
 		if err := cli.CmdRun("-i", "-t", unitTestImageID, "/bin/cat"); err != nil {
-			utils.Debugf("Error CmdRun: %s\n", err)
+			utils.Debugf("Error CmdRun: %s", err)
 		}
 		}
 
 
 		close(c1)
 		close(c1)
@@ -393,12 +435,14 @@ func TestRunDetach(t *testing.T) {
 	container := globalRuntime.List()[0]
 	container := globalRuntime.List()[0]
 
 
 	setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
 	setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
-		stdinPipe.Write([]byte{'', ''})
+		stdinPipe.Write([]byte{16, 17})
 		if err := stdinPipe.Close(); err != nil {
 		if err := stdinPipe.Close(); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 	})
 	})
 
 
+	closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
+
 	// wait for CmdRun to return
 	// wait for CmdRun to return
 	setTimeout(t, "Waiting for CmdRun timed out", 15*time.Second, func() {
 	setTimeout(t, "Waiting for CmdRun timed out", 15*time.Second, func() {
 		<-ch
 		<-ch
@@ -411,7 +455,6 @@ func TestRunDetach(t *testing.T) {
 
 
 	setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() {
 	setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() {
 		container.Kill()
 		container.Kill()
-		container.Wait()
 	})
 	})
 }
 }
 
 
@@ -423,39 +466,62 @@ func TestAttachDetach(t *testing.T) {
 	cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
 	cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
 	defer cleanup(globalRuntime)
 	defer cleanup(globalRuntime)
 
 
-	go stdout.Read(make([]byte, 1024))
-	setTimeout(t, "Starting container timed out", 2*time.Second, func() {
+	ch := make(chan struct{})
+	go func() {
+		defer close(ch)
 		if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
 		if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
-	})
+	}()
 
 
-	container := globalRuntime.List()[0]
+	var container *Container
+
+	setTimeout(t, "Reading container's id timed out", 10*time.Second, func() {
+		buf := make([]byte, 1024)
+		n, err := stdout.Read(buf)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		container = globalRuntime.List()[0]
+
+		if strings.Trim(string(buf[:n]), " \r\n") != container.ShortID() {
+			t.Fatalf("Wrong ID received. Expect %s, received %s", container.ShortID(), buf[:n])
+		}
+	})
+	setTimeout(t, "Starting container timed out", 10*time.Second, func() {
+		<-ch
+	})
 
 
 	stdin, stdinPipe = io.Pipe()
 	stdin, stdinPipe = io.Pipe()
 	stdout, stdoutPipe = io.Pipe()
 	stdout, stdoutPipe = io.Pipe()
 	cli = NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
 	cli = NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
 
 
-	ch := make(chan struct{})
+	ch = make(chan struct{})
 	go func() {
 	go func() {
 		defer close(ch)
 		defer close(ch)
 		if err := cli.CmdAttach(container.ShortID()); err != nil {
 		if err := cli.CmdAttach(container.ShortID()); err != nil {
-			t.Fatal(err)
+			if err != io.ErrClosedPipe {
+				t.Fatal(err)
+			}
 		}
 		}
 	}()
 	}()
 
 
 	setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
 	setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
 		if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
 		if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
-			t.Fatal(err)
+			if err != io.ErrClosedPipe {
+				t.Fatal(err)
+			}
 		}
 		}
 	})
 	})
 
 
 	setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
 	setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
-		stdinPipe.Write([]byte{'', ''})
+		stdinPipe.Write([]byte{16, 17})
 		if err := stdinPipe.Close(); err != nil {
 		if err := stdinPipe.Close(); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 	})
 	})
+	closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
 
 
 	// wait for CmdRun to return
 	// wait for CmdRun to return
 	setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() {
 	setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() {
@@ -469,7 +535,6 @@ func TestAttachDetach(t *testing.T) {
 
 
 	setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
 	setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
 		container.Kill()
 		container.Kill()
-		container.Wait()
 	})
 	})
 }
 }
 
 
@@ -484,7 +549,7 @@ func TestAttachDisconnect(t *testing.T) {
 	go func() {
 	go func() {
 		// Start a process in daemon mode
 		// Start a process in daemon mode
 		if err := cli.CmdRun("-d", "-i", unitTestImageID, "/bin/cat"); err != nil {
 		if err := cli.CmdRun("-d", "-i", unitTestImageID, "/bin/cat"); err != nil {
-			utils.Debugf("Error CmdRun: %s\n", err)
+			utils.Debugf("Error CmdRun: %s", err)
 		}
 		}
 	}()
 	}()
 
 
@@ -570,7 +635,7 @@ func TestRunAutoRemove(t *testing.T) {
 		}
 		}
 	})
 	})
 
 
-	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
+	setTimeout(t, "CmdRun timed out", 10*time.Second, func() {
 		<-c
 		<-c
 	})
 	})
 
 

+ 18 - 0
config.go

@@ -0,0 +1,18 @@
+package docker
+
+import (
+	"net"
+)
+
+type DaemonConfig struct {
+	Pidfile                     string
+	GraphPath                   string
+	ProtoAddresses              []string
+	AutoRestart                 bool
+	EnableCors                  bool
+	Dns                         []string
+	EnableIptables              bool
+	BridgeIface                 string
+	DefaultIp                   net.IP
+	InterContainerCommunication bool
+}

+ 275 - 85
container.go

@@ -1,6 +1,7 @@
 package docker
 package docker
 
 
 import (
 import (
+	"bytes"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"flag"
 	"flag"
@@ -43,6 +44,7 @@ type Container struct {
 	ResolvConfPath string
 	ResolvConfPath string
 	HostnamePath   string
 	HostnamePath   string
 	HostsPath      string
 	HostsPath      string
+	Name           string
 
 
 	cmd       *exec.Cmd
 	cmd       *exec.Cmd
 	stdout    *utils.WriteBroadcaster
 	stdout    *utils.WriteBroadcaster
@@ -58,6 +60,8 @@ type Container struct {
 	// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
 	// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
 	// Easier than migrating older container configs :)
 	// Easier than migrating older container configs :)
 	VolumesRW map[string]bool
 	VolumesRW map[string]bool
+
+	activeLinks map[string]*Link
 }
 }
 
 
 type Config struct {
 type Config struct {
@@ -70,7 +74,8 @@ type Config struct {
 	AttachStdin     bool
 	AttachStdin     bool
 	AttachStdout    bool
 	AttachStdout    bool
 	AttachStderr    bool
 	AttachStderr    bool
-	PortSpecs       []string
+	PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp
+	ExposedPorts    map[Port]struct{}
 	Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.
 	Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.
 	OpenStdin       bool // Open stdin
 	OpenStdin       bool // Open stdin
 	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
 	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
@@ -90,6 +95,8 @@ type HostConfig struct {
 	Binds           []string
 	Binds           []string
 	ContainerIDFile string
 	ContainerIDFile string
 	LxcConf         []KeyValuePair
 	LxcConf         []KeyValuePair
+	PortBindings    map[Port][]PortBinding
+	Links           []string
 }
 }
 
 
 type BindMap struct {
 type BindMap struct {
@@ -99,7 +106,11 @@ type BindMap struct {
 }
 }
 
 
 var (
 var (
-	ErrInvaidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.")
+	ErrContainerStart           = errors.New("The container failed to start. Unkown error")
+	ErrContainerStartTimeout    = errors.New("The container failed to start due to timed out.")
+	ErrInvalidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.")
+	ErrConflictAttachDetach     = errors.New("Conflicting options: -a and -d")
+	ErrConflictDetachAutoRemove = errors.New("Conflicting options: -rm and -d")
 )
 )
 
 
 type KeyValuePair struct {
 type KeyValuePair struct {
@@ -107,6 +118,34 @@ type KeyValuePair struct {
 	Value string
 	Value string
 }
 }
 
 
+type PortBinding struct {
+	HostIp   string
+	HostPort string
+}
+
+// 80/tcp
+type Port string
+
+func (p Port) Proto() string {
+	return strings.Split(string(p), "/")[1]
+}
+
+func (p Port) Port() string {
+	return strings.Split(string(p), "/")[0]
+}
+
+func (p Port) Int() int {
+	i, err := parsePort(p.Port())
+	if err != nil {
+		panic(err)
+	}
+	return i
+}
+
+func NewPort(proto, port string) Port {
+	return Port(fmt.Sprintf("%s/%s", port, proto))
+}
+
 func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
 func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
 	if os.Getenv("TEST") != "" {
 	if os.Getenv("TEST") != "" {
@@ -127,6 +166,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 	flNetwork := cmd.Bool("n", true, "Enable networking for this container")
 	flNetwork := cmd.Bool("n", true, "Enable networking for this container")
 	flPrivileged := cmd.Bool("privileged", false, "Give extended privileges to this container")
 	flPrivileged := cmd.Bool("privileged", false, "Give extended privileges to this container")
 	flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
 	flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
+	cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)")
+	cmd.String("name", "", "Assign a name to the container")
 
 
 	if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
 	if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
 		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
 		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
@@ -135,35 +176,45 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 
 
 	flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)")
 	flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)")
 
 
-	var flPorts ListOpts
-	cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)")
+	var flPublish utils.ListOpts
+	cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)")
 
 
-	var flEnv ListOpts
+	var flExpose utils.ListOpts
+	cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host")
+
+	var flEnv utils.ListOpts
 	cmd.Var(&flEnv, "e", "Set environment variables")
 	cmd.Var(&flEnv, "e", "Set environment variables")
 
 
-	var flDns ListOpts
+	var flDns utils.ListOpts
 	cmd.Var(&flDns, "dns", "Set custom dns servers")
 	cmd.Var(&flDns, "dns", "Set custom dns servers")
 
 
 	flVolumes := NewPathOpts()
 	flVolumes := NewPathOpts()
 	cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
 	cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
 
 
-	var flVolumesFrom ListOpts
+	var flVolumesFrom utils.ListOpts
 	cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container")
 	cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container")
 
 
 	flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
 	flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
 
 
-	var flLxcOpts ListOpts
+	var flLxcOpts utils.ListOpts
 	cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
 	cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
 
 
+	var flLinks utils.ListOpts
+	cmd.Var(&flLinks, "link", "Add link to another container (name:alias)")
+
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil, nil, cmd, err
 		return nil, nil, cmd, err
 	}
 	}
 	if *flDetach && len(flAttach) > 0 {
 	if *flDetach && len(flAttach) > 0 {
-		return nil, nil, cmd, fmt.Errorf("Conflicting options: -a and -d")
+		return nil, nil, cmd, ErrConflictAttachDetach
 	}
 	}
 	if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
 	if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
-		return nil, nil, cmd, ErrInvaidWorikingDirectory
+		return nil, nil, cmd, ErrInvalidWorikingDirectory
+	}
+	if *flDetach && *flAutoRemove {
+		return nil, nil, cmd, ErrConflictDetachAutoRemove
 	}
 	}
+
 	// If neither -d or -a are set, attach to everything by default
 	// If neither -d or -a are set, attach to everything by default
 	if len(flAttach) == 0 && !*flDetach {
 	if len(flAttach) == 0 && !*flDetach {
 		if !*flDetach {
 		if !*flDetach {
@@ -175,10 +226,6 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 		}
 		}
 	}
 	}
 
 
-	if *flDetach && *flAutoRemove {
-		return nil, nil, cmd, fmt.Errorf("Conflicting options: -rm and -d")
-	}
-
 	var binds []string
 	var binds []string
 
 
 	// add any bind targets to the list of container volumes
 	// add any bind targets to the list of container volumes
@@ -220,10 +267,28 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 		hostname = parts[0]
 		hostname = parts[0]
 		domainname = parts[1]
 		domainname = parts[1]
 	}
 	}
+
+	ports, portBindings, err := parsePortSpecs(flPublish)
+	if err != nil {
+		return nil, nil, cmd, err
+	}
+
+	// Merge in exposed ports to the map of published ports
+	for _, e := range flExpose {
+		if strings.Contains(e, ":") {
+			return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e)
+		}
+		p := NewPort(splitProtoPort(e))
+		if _, exists := ports[p]; !exists {
+			ports[p] = struct{}{}
+		}
+	}
+
 	config := &Config{
 	config := &Config{
-		Hostname:        hostname,
+		Hostname:        *flHostname,
 		Domainname:      domainname,
 		Domainname:      domainname,
-		PortSpecs:       flPorts,
+		PortSpecs:       nil, // Deprecated
+		ExposedPorts:    ports,
 		User:            *flUser,
 		User:            *flUser,
 		Tty:             *flTty,
 		Tty:             *flTty,
 		NetworkDisabled: !*flNetwork,
 		NetworkDisabled: !*flNetwork,
@@ -243,10 +308,13 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 		Privileged:      *flPrivileged,
 		Privileged:      *flPrivileged,
 		WorkingDir:      *flWorkingDir,
 		WorkingDir:      *flWorkingDir,
 	}
 	}
+
 	hostConfig := &HostConfig{
 	hostConfig := &HostConfig{
 		Binds:           binds,
 		Binds:           binds,
 		ContainerIDFile: *flContainerIDFile,
 		ContainerIDFile: *flContainerIDFile,
 		LxcConf:         lxcConf,
 		LxcConf:         lxcConf,
+		PortBindings:    portBindings,
+		Links:           flLinks,
 	}
 	}
 
 
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
@@ -261,36 +329,38 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 	return config, hostConfig, cmd, nil
 	return config, hostConfig, cmd, nil
 }
 }
 
 
-type PortMapping map[string]string
+type PortMapping map[string]string // Deprecated
 
 
 type NetworkSettings struct {
 type NetworkSettings struct {
 	IPAddress   string
 	IPAddress   string
 	IPPrefixLen int
 	IPPrefixLen int
 	Gateway     string
 	Gateway     string
 	Bridge      string
 	Bridge      string
-	PortMapping map[string]PortMapping
+	PortMapping map[string]PortMapping // Deprecated
+	Ports       map[Port][]PortBinding
 }
 }
 
 
-// returns a more easy to process description of the port mapping defined in the settings
 func (settings *NetworkSettings) PortMappingAPI() []APIPort {
 func (settings *NetworkSettings) PortMappingAPI() []APIPort {
 	var mapping []APIPort
 	var mapping []APIPort
-	for private, public := range settings.PortMapping["Tcp"] {
-		pubint, _ := strconv.ParseInt(public, 0, 0)
-		privint, _ := strconv.ParseInt(private, 0, 0)
-		mapping = append(mapping, APIPort{
-			PrivatePort: privint,
-			PublicPort:  pubint,
-			Type:        "tcp",
-		})
-	}
-	for private, public := range settings.PortMapping["Udp"] {
-		pubint, _ := strconv.ParseInt(public, 0, 0)
-		privint, _ := strconv.ParseInt(private, 0, 0)
-		mapping = append(mapping, APIPort{
-			PrivatePort: privint,
-			PublicPort:  pubint,
-			Type:        "udp",
-		})
+	for port, bindings := range settings.Ports {
+		p, _ := parsePort(port.Port())
+		if len(bindings) == 0 {
+			mapping = append(mapping, APIPort{
+				PublicPort: int64(p),
+				Type:       port.Proto(),
+			})
+			continue
+		}
+		for _, binding := range bindings {
+			p, _ := parsePort(port.Port())
+			h, _ := parsePort(binding.HostPort)
+			mapping = append(mapping, APIPort{
+				PrivatePort: int64(p),
+				PublicPort:  int64(h),
+				Type:        port.Proto(),
+				IP:          binding.HostIp,
+			})
+		}
 	}
 	}
 	return mapping
 	return mapping
 }
 }
@@ -460,11 +530,13 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
 				} else {
 				} else {
 					_, err = io.Copy(cStdin, stdin)
 					_, err = io.Copy(cStdin, stdin)
 				}
 				}
+				if err == io.ErrClosedPipe {
+					err = nil
+				}
 				if err != nil {
 				if err != nil {
 					utils.Errorf("attach: stdin: %s", err)
 					utils.Errorf("attach: stdin: %s", err)
 				}
 				}
-				// Discard error, expecting pipe error
-				errors <- nil
+				errors <- err
 			}()
 			}()
 		}
 		}
 	}
 	}
@@ -590,7 +662,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
 	if container.runtime.networkManager.disabled {
 	if container.runtime.networkManager.disabled {
 		container.Config.NetworkDisabled = true
 		container.Config.NetworkDisabled = true
 	} else {
 	} else {
-		if err := container.allocateNetwork(); err != nil {
+		if err := container.allocateNetwork(hostConfig); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -780,6 +852,46 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
 		"-e", "container=lxc",
 		"-e", "container=lxc",
 		"-e", "HOSTNAME="+container.Config.Hostname,
 		"-e", "HOSTNAME="+container.Config.Hostname,
 	)
 	)
+
+	// Init any links between the parent and children
+	runtime := container.runtime
+
+	children, err := runtime.Children(container.Name)
+	if err != nil {
+		return err
+	}
+
+	if len(children) > 0 {
+		container.activeLinks = make(map[string]*Link, len(children))
+
+		// If we encounter an error make sure that we rollback any network
+		// config and ip table changes
+		rollback := func() {
+			for _, link := range container.activeLinks {
+				link.Disable()
+			}
+			container.activeLinks = nil
+		}
+
+		for p, child := range children {
+			link, err := NewLink(container, child, p, runtime.networkManager.bridgeIface)
+			if err != nil {
+				rollback()
+				return err
+			}
+
+			container.activeLinks[link.Alias()] = link
+			if err := link.Enable(); err != nil {
+				rollback()
+				return err
+			}
+
+			for _, envVar := range link.ToEnv() {
+				params = append(params, "-e", envVar)
+			}
+		}
+	}
+
 	if container.Config.WorkingDir != "" {
 	if container.Config.WorkingDir != "" {
 		workingDir := path.Clean(container.Config.WorkingDir)
 		workingDir := path.Clean(container.Config.WorkingDir)
 		utils.Debugf("[working dir] working dir is %s", workingDir)
 		utils.Debugf("[working dir] working dir is %s", workingDir)
@@ -831,12 +943,43 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
 	container.ToDisk()
 	container.ToDisk()
 	container.SaveHostConfig(hostConfig)
 	container.SaveHostConfig(hostConfig)
 	go container.monitor(hostConfig)
 	go container.monitor(hostConfig)
-	return nil
+
+	defer utils.Debugf("Container running: %v", container.State.Running)
+	// We wait for the container to be fully running.
+	// Timeout after 5 seconds. In case of broken pipe, just retry.
+	// Note: The container can run and finish correctly before
+	//       the end of this loop
+	for now := time.Now(); time.Since(now) < 5*time.Second; {
+		// If the container dies while waiting for it, just return
+		if !container.State.Running {
+			return nil
+		}
+		output, err := exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput()
+		if err != nil {
+			utils.Debugf("Error with lxc-info: %s (%s)", err, output)
+
+			output, err = exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput()
+			if err != nil {
+				utils.Debugf("Second Error with lxc-info: %s (%s)", err, output)
+				return err
+			}
+
+		}
+		if strings.Contains(string(output), "RUNNING") {
+			return nil
+		}
+		utils.Debugf("Waiting for the container to start (running: %v): %s", container.State.Running, bytes.TrimSpace(output))
+		time.Sleep(50 * time.Millisecond)
+	}
+
+	if container.State.Running {
+		return ErrContainerStartTimeout
+	}
+	return ErrContainerStart
 }
 }
 
 
 func (container *Container) Run() error {
 func (container *Container) Run() error {
-	hostConfig := &HostConfig{}
-	if err := container.Start(hostConfig); err != nil {
+	if err := container.Start(&HostConfig{}); err != nil {
 		return err
 		return err
 	}
 	}
 	container.Wait()
 	container.Wait()
@@ -882,7 +1025,7 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
 	return utils.NewBufReader(reader), nil
 	return utils.NewBufReader(reader), nil
 }
 }
 
 
-func (container *Container) allocateNetwork() error {
+func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
 	if container.Config.NetworkDisabled {
 	if container.Config.NetworkDisabled {
 		return nil
 		return nil
 	}
 	}
@@ -909,36 +1052,59 @@ func (container *Container) allocateNetwork() error {
 		}
 		}
 	}
 	}
 
 
-	var portSpecs []string
+	if container.Config.PortSpecs != nil {
+		utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
+		if err := migratePortMappings(container.Config); err != nil {
+			return err
+		}
+		container.Config.PortSpecs = nil
+	}
+
+	portSpecs := make(map[Port]struct{})
+	bindings := make(map[Port][]PortBinding)
+
 	if !container.State.Ghost {
 	if !container.State.Ghost {
-		portSpecs = container.Config.PortSpecs
-	} else {
-		for backend, frontend := range container.NetworkSettings.PortMapping["Tcp"] {
-			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/tcp", frontend, backend))
+		if container.Config.ExposedPorts != nil {
+			portSpecs = container.Config.ExposedPorts
+		}
+		if hostConfig.PortBindings != nil {
+			bindings = hostConfig.PortBindings
 		}
 		}
-		for backend, frontend := range container.NetworkSettings.PortMapping["Udp"] {
-			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/udp", frontend, backend))
+	} else {
+		if container.NetworkSettings.Ports != nil {
+			for port, binding := range container.NetworkSettings.Ports {
+				portSpecs[port] = struct{}{}
+				bindings[port] = binding
+			}
 		}
 		}
 	}
 	}
 
 
-	container.NetworkSettings.PortMapping = make(map[string]PortMapping)
-	container.NetworkSettings.PortMapping["Tcp"] = make(PortMapping)
-	container.NetworkSettings.PortMapping["Udp"] = make(PortMapping)
-	for _, spec := range portSpecs {
-		nat, err := iface.AllocatePort(spec)
-		if err != nil {
-			iface.Release()
-			return err
+	container.NetworkSettings.PortMapping = nil
+
+	for port := range portSpecs {
+		binding := bindings[port]
+		for i := 0; i < len(binding); i++ {
+			b := binding[i]
+			nat, err := iface.AllocatePort(port, b)
+			if err != nil {
+				iface.Release()
+				return err
+			}
+			utils.Debugf("Allocate port: %s:%s->%s", nat.Binding.HostIp, port, nat.Binding.HostPort)
+			binding[i] = nat.Binding
 		}
 		}
-		proto := strings.Title(nat.Proto)
-		backend, frontend := strconv.Itoa(nat.Backend), strconv.Itoa(nat.Frontend)
-		container.NetworkSettings.PortMapping[proto][backend] = frontend
+		bindings[port] = binding
 	}
 	}
+	container.SaveHostConfig(hostConfig)
+
+	container.NetworkSettings.Ports = bindings
 	container.network = iface
 	container.network = iface
+
 	container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
 	container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
 	container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
 	container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
 	container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
 	container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
 	container.NetworkSettings.Gateway = iface.Gateway.String()
 	container.NetworkSettings.Gateway = iface.Gateway.String()
+
 	return nil
 	return nil
 }
 }
 
 
@@ -951,7 +1117,7 @@ func (container *Container) releaseNetwork() {
 	container.NetworkSettings = &NetworkSettings{}
 	container.NetworkSettings = &NetworkSettings{}
 }
 }
 
 
-// FIXME: replace this with a control socket within docker-init
+// FIXME: replace this with a control socket within dockerinit
 func (container *Container) waitLxc() error {
 func (container *Container) waitLxc() error {
 	for {
 	for {
 		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
 		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
@@ -1021,6 +1187,14 @@ func (container *Container) monitor(hostConfig *HostConfig) {
 
 
 func (container *Container) cleanup() {
 func (container *Container) cleanup() {
 	container.releaseNetwork()
 	container.releaseNetwork()
+
+	// Disable all active links
+	if container.activeLinks != nil {
+		for _, link := range container.activeLinks {
+			link.Disable()
+		}
+	}
+
 	if container.Config.OpenStdin {
 	if container.Config.OpenStdin {
 		if err := container.stdin.Close(); err != nil {
 		if err := container.stdin.Close(); err != nil {
 			utils.Errorf("%s: Error close stdin: %s", container.ID, err)
 			utils.Errorf("%s: Error close stdin: %s", container.ID, err)
@@ -1044,54 +1218,57 @@ func (container *Container) cleanup() {
 	}
 	}
 }
 }
 
 
-func (container *Container) kill() error {
+func (container *Container) kill(sig int) error {
+	container.State.Lock()
+	defer container.State.Unlock()
+
 	if !container.State.Running {
 	if !container.State.Running {
 		return nil
 		return nil
 	}
 	}
 
 
-	// Sending SIGKILL to the process via lxc
-	output, err := exec.Command("lxc-kill", "-n", container.ID, "9").CombinedOutput()
-	if err != nil {
-		log.Printf("error killing container %s (%s, %s)", container.ID, output, err)
+	if output, err := exec.Command("lxc-kill", "-n", container.ID, strconv.Itoa(sig)).CombinedOutput(); err != nil {
+		log.Printf("error killing container %s (%s, %s)", container.ShortID(), output, err)
+		return err
+	}
+
+	return nil
+}
+
+func (container *Container) Kill() error {
+	if !container.State.Running {
+		return nil
+	}
+
+	// 1. Send SIGKILL
+	if err := container.kill(9); err != nil {
+		return err
 	}
 	}
 
 
 	// 2. Wait for the process to die, in last resort, try to kill the process directly
 	// 2. Wait for the process to die, in last resort, try to kill the process directly
 	if err := container.WaitTimeout(10 * time.Second); err != nil {
 	if err := container.WaitTimeout(10 * time.Second); err != nil {
 		if container.cmd == nil {
 		if container.cmd == nil {
-			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ID)
+			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ShortID())
 		}
 		}
-		log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.ID)
+		log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", container.ShortID())
 		if err := container.cmd.Process.Kill(); err != nil {
 		if err := container.cmd.Process.Kill(); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
 
 
-	// Wait for the container to be actually stopped
 	container.Wait()
 	container.Wait()
 	return nil
 	return nil
 }
 }
 
 
-func (container *Container) Kill() error {
-	container.State.Lock()
-	defer container.State.Unlock()
-	if !container.State.Running {
-		return nil
-	}
-	return container.kill()
-}
-
 func (container *Container) Stop(seconds int) error {
 func (container *Container) Stop(seconds int) error {
-	container.State.Lock()
-	defer container.State.Unlock()
 	if !container.State.Running {
 	if !container.State.Running {
 		return nil
 		return nil
 	}
 	}
 
 
 	// 1. Send a SIGTERM
 	// 1. Send a SIGTERM
-	if output, err := exec.Command("lxc-kill", "-n", container.ID, "15").CombinedOutput(); err != nil {
-		log.Print(string(output))
+	if err := container.kill(15); err != nil {
+		utils.Debugf("Error sending kill SIGTERM: %s", err)
 		log.Print("Failed to send SIGTERM to the process, force killing")
 		log.Print("Failed to send SIGTERM to the process, force killing")
-		if err := container.kill(); err != nil {
+		if err := container.kill(9); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -1099,7 +1276,8 @@ func (container *Container) Stop(seconds int) error {
 	// 2. Wait for the process to exit on its own
 	// 2. Wait for the process to exit on its own
 	if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
 	if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
 		log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
 		log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
-		if err := container.kill(); err != nil {
+		// 3. If it doesn't, then send SIGKILL
+		if err := container.Kill(); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -1202,6 +1380,12 @@ func (container *Container) Mounted() (bool, error) {
 }
 }
 
 
 func (container *Container) Unmount() error {
 func (container *Container) Unmount() error {
+	if _, err := os.Stat(container.RootfsPath()); err != nil {
+		if os.IsNotExist(err) {
+			return nil
+		}
+		return err
+	}
 	return Unmount(container.RootfsPath())
 	return Unmount(container.RootfsPath())
 }
 }
 
 
@@ -1292,3 +1476,9 @@ func (container *Container) Copy(resource string) (Archive, error) {
 	}
 	}
 	return TarFilter(basePath, Uncompressed, filter)
 	return TarFilter(basePath, Uncompressed, filter)
 }
 }
+
+// Returns true if the container exposes a certain port
+func (container *Container) Exposes(p Port) bool {
+	_, exists := container.Config.ExposedPorts[p]
+	return exists
+}

+ 91 - 40
container_test.go

@@ -18,11 +18,12 @@ import (
 func TestIDFormat(t *testing.T) {
 func TestIDFormat(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container1, err := runtime.Create(
+	container1, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -388,11 +389,12 @@ func TestRun(t *testing.T) {
 func TestOutput(t *testing.T) {
 func TestOutput(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foobar"},
 			Cmd:   []string{"echo", "-n", "foobar"},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -407,16 +409,39 @@ func TestOutput(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestContainerNetwork(t *testing.T) {
+	runtime := mkRuntime(t)
+	defer nuke(runtime)
+	container, _, err := runtime.Create(
+		&Config{
+			Image: GetTestImage(runtime).ID,
+			Cmd:   []string{"ping", "-c", "1", "127.0.0.1"},
+		},
+		"",
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer runtime.Destroy(container)
+	if err := container.Run(); err != nil {
+		t.Fatal(err)
+	}
+	if container.State.ExitCode != 0 {
+		t.Errorf("Unexpected ping 127.0.0.1 exit code %d (expected 0)", container.State.ExitCode)
+	}
+}
+
 func TestKillDifferentUser(t *testing.T) {
 func TestKillDifferentUser(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:     GetTestImage(runtime).ID,
 		Image:     GetTestImage(runtime).ID,
 		Cmd:       []string{"cat"},
 		Cmd:       []string{"cat"},
 		OpenStdin: true,
 		OpenStdin: true,
 		User:      "daemon",
 		User:      "daemon",
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -471,7 +496,7 @@ func TestCreateVolume(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	c, err := runtime.Create(config)
+	c, _, err := runtime.Create(config, "")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -486,10 +511,11 @@ func TestCreateVolume(t *testing.T) {
 func TestKill(t *testing.T) {
 func TestKill(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 		Cmd:   []string{"sleep", "2"},
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -530,10 +556,10 @@ func TestExitCode(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	trueContainer, err := runtime.Create(&Config{
+	trueContainer, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true", ""},
 		Cmd:   []string{"/bin/true", ""},
-	})
+	}, "")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -545,10 +571,10 @@ func TestExitCode(t *testing.T) {
 		t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode)
 		t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode)
 	}
 	}
 
 
-	falseContainer, err := runtime.Create(&Config{
+	falseContainer, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/false", ""},
 		Cmd:   []string{"/bin/false", ""},
-	})
+	}, "")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -564,10 +590,11 @@ func TestExitCode(t *testing.T) {
 func TestRestart(t *testing.T) {
 func TestRestart(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"echo", "-n", "foobar"},
 		Cmd:   []string{"echo", "-n", "foobar"},
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -594,12 +621,13 @@ func TestRestart(t *testing.T) {
 func TestRestartStdin(t *testing.T) {
 func TestRestartStdin(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 		Cmd:   []string{"cat"},
 
 
 		OpenStdin: true,
 		OpenStdin: true,
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -672,10 +700,11 @@ func TestUser(t *testing.T) {
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
 	// Default user must be root
 	// Default user must be root
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -690,12 +719,13 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Set a username
 	// Set a username
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
 		User: "root",
 		User: "root",
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -710,12 +740,13 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Set a UID
 	// Set a UID
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
 		User: "0",
 		User: "0",
 	},
 	},
+		"",
 	)
 	)
 	if err != nil || container.State.ExitCode != 0 {
 	if err != nil || container.State.ExitCode != 0 {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -730,12 +761,13 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Set a different user by uid
 	// Set a different user by uid
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
 		User: "1",
 		User: "1",
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -752,12 +784,13 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Set a different user by username
 	// Set a different user by username
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
 		User: "daemon",
 		User: "daemon",
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -772,12 +805,13 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Test an wrong username
 	// Test an wrong username
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
 		User: "unknownuser",
 		User: "unknownuser",
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -793,20 +827,22 @@ func TestMultipleContainers(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container1, err := runtime.Create(&Config{
+	container1, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 		Cmd:   []string{"sleep", "2"},
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	defer runtime.Destroy(container1)
 	defer runtime.Destroy(container1)
 
 
-	container2, err := runtime.Create(&Config{
+	container2, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 		Cmd:   []string{"sleep", "2"},
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -847,12 +883,13 @@ func TestMultipleContainers(t *testing.T) {
 func TestStdin(t *testing.T) {
 func TestStdin(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 		Cmd:   []string{"cat"},
 
 
 		OpenStdin: true,
 		OpenStdin: true,
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -892,12 +929,13 @@ func TestStdin(t *testing.T) {
 func TestTty(t *testing.T) {
 func TestTty(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 		Cmd:   []string{"cat"},
 
 
 		OpenStdin: true,
 		OpenStdin: true,
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -937,10 +975,11 @@ func TestTty(t *testing.T) {
 func TestEnv(t *testing.T) {
 func TestEnv(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"env"},
 		Cmd:   []string{"env"},
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -986,12 +1025,13 @@ func TestEnv(t *testing.T) {
 func TestEntrypoint(t *testing.T) {
 func TestEntrypoint(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:      GetTestImage(runtime).ID,
 			Image:      GetTestImage(runtime).ID,
 			Entrypoint: []string{"/bin/echo"},
 			Entrypoint: []string{"/bin/echo"},
 			Cmd:        []string{"-n", "foobar"},
 			Cmd:        []string{"-n", "foobar"},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1009,11 +1049,12 @@ func TestEntrypoint(t *testing.T) {
 func TestEntrypointNoCmd(t *testing.T) {
 func TestEntrypointNoCmd(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:      GetTestImage(runtime).ID,
 			Image:      GetTestImage(runtime).ID,
 			Entrypoint: []string{"/bin/echo", "foobar"},
 			Entrypoint: []string{"/bin/echo", "foobar"},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1060,7 +1101,7 @@ func TestLXCConfig(t *testing.T) {
 	cpuMin := 100
 	cpuMin := 100
 	cpuMax := 10000
 	cpuMax := 10000
 	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
 	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true"},
 		Cmd:   []string{"/bin/true"},
 
 
@@ -1068,6 +1109,7 @@ func TestLXCConfig(t *testing.T) {
 		Memory:    int64(mem),
 		Memory:    int64(mem),
 		CpuShares: int64(cpu),
 		CpuShares: int64(cpu),
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1084,12 +1126,13 @@ func TestLXCConfig(t *testing.T) {
 func TestCustomLxcConfig(t *testing.T) {
 func TestCustomLxcConfig(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true"},
 		Cmd:   []string{"/bin/true"},
 
 
 		Hostname: "foobar",
 		Hostname: "foobar",
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1115,10 +1158,11 @@ func BenchmarkRunSequencial(b *testing.B) {
 	runtime := mkRuntime(b)
 	runtime := mkRuntime(b)
 	defer nuke(runtime)
 	defer nuke(runtime)
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {
-		container, err := runtime.Create(&Config{
+		container, _, err := runtime.Create(&Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foo"},
 			Cmd:   []string{"echo", "-n", "foo"},
 		},
 		},
+			"",
 		)
 		)
 		if err != nil {
 		if err != nil {
 			b.Fatal(err)
 			b.Fatal(err)
@@ -1147,10 +1191,11 @@ func BenchmarkRunParallel(b *testing.B) {
 		complete := make(chan error)
 		complete := make(chan error)
 		tasks = append(tasks, complete)
 		tasks = append(tasks, complete)
 		go func(i int, complete chan error) {
 		go func(i int, complete chan error) {
-			container, err := runtime.Create(&Config{
+			container, _, err := runtime.Create(&Config{
 				Image: GetTestImage(runtime).ID,
 				Image: GetTestImage(runtime).ID,
 				Cmd:   []string{"echo", "-n", "foo"},
 				Cmd:   []string{"echo", "-n", "foo"},
 			},
 			},
+				"",
 			)
 			)
 			if err != nil {
 			if err != nil {
 				complete <- err
 				complete <- err
@@ -1189,7 +1234,7 @@ func BenchmarkRunParallel(b *testing.B) {
 }
 }
 
 
 func tempDir(t *testing.T) string {
 func tempDir(t *testing.T) string {
-	tmpDir, err := ioutil.TempDir("", "docker-test")
+	tmpDir, err := ioutil.TempDir("", "docker-test-container")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1297,12 +1342,13 @@ func TestBindMounts(t *testing.T) {
 func TestVolumesFromReadonlyMount(t *testing.T) {
 func TestVolumesFromReadonlyMount(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:   GetTestImage(runtime).ID,
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
 			Volumes: map[string]struct{}{"/test": {}},
 			Volumes: map[string]struct{}{"/test": {}},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1316,12 +1362,13 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
 		t.Fail()
 		t.Fail()
 	}
 	}
 
 
-	container2, err := runtime.Create(
+	container2, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:       GetTestImage(runtime).ID,
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			VolumesFrom: container.ID,
 			VolumesFrom: container.ID,
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1352,11 +1399,12 @@ func TestRestartWithVolumes(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:   GetTestImage(runtime).ID,
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"echo", "-n", "foobar"},
 		Cmd:     []string{"echo", "-n", "foobar"},
 		Volumes: map[string]struct{}{"/test": {}},
 		Volumes: map[string]struct{}{"/test": {}},
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1395,11 +1443,12 @@ func TestVolumesFromWithVolumes(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:   GetTestImage(runtime).ID,
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Volumes: map[string]struct{}{"/test": {}},
 		Volumes: map[string]struct{}{"/test": {}},
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1422,13 +1471,14 @@ func TestVolumesFromWithVolumes(t *testing.T) {
 		t.Fail()
 		t.Fail()
 	}
 	}
 
 
-	container2, err := runtime.Create(
+	container2, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:       GetTestImage(runtime).ID,
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"cat", "/test/foo"},
 			Cmd:         []string{"cat", "/test/foo"},
 			VolumesFrom: container.ID,
 			VolumesFrom: container.ID,
 			Volumes:     map[string]struct{}{"/test": {}},
 			Volumes:     map[string]struct{}{"/test": {}},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1463,7 +1513,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	c, err := runtime.Create(config)
+	c, _, err := runtime.Create(config, "")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1529,11 +1579,12 @@ func TestMultipleVolumesFrom(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:   GetTestImage(runtime).ID,
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Volumes: map[string]struct{}{"/test": {}},
 		Volumes: map[string]struct{}{"/test": {}},
 	},
 	},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1556,12 +1607,13 @@ func TestMultipleVolumesFrom(t *testing.T) {
 		t.Fail()
 		t.Fail()
 	}
 	}
 
 
-	container2, err := runtime.Create(
+	container2, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:   GetTestImage(runtime).ID,
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"sh", "-c", "echo -n bar > /other/foo"},
 			Cmd:     []string{"sh", "-c", "echo -n bar > /other/foo"},
 			Volumes: map[string]struct{}{"/other": {}},
 			Volumes: map[string]struct{}{"/other": {}},
 		},
 		},
+		"",
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1577,12 +1629,12 @@ func TestMultipleVolumesFrom(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	container3, err := runtime.Create(
+	container3, _, err := runtime.Create(
 		&Config{
 		&Config{
 			Image:       GetTestImage(runtime).ID,
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
 			VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
-		})
+		}, "")
 
 
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1593,7 +1645,6 @@ func TestMultipleVolumesFrom(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	t.Log(container3.Volumes)
 	if container3.Volumes["/test"] != container.Volumes["/test"] {
 	if container3.Volumes["/test"] != container.Volumes["/test"] {
 		t.Fail()
 		t.Fail()
 	}
 	}

+ 0 - 1
contrib/brew/.gitignore

@@ -1 +0,0 @@
-*.pyc

+ 0 - 78
contrib/brew/README.md

@@ -1,78 +0,0 @@
-# docker-brew
-
-docker-brew is a command-line tool used to build the docker standard library.
-
-## Install instructions
-
-1. Install python if it isn't already available on your OS of choice
-1. Install the easy_install tool (`sudo apt-get install python-setuptools`
-for Debian)
-1. Install the python package manager, `pip` (`easy_install pip`)
-1. Run the following command: `sudo pip install -r requirements.txt`
-1. You should now be able to use the `docker-brew` script as such.
-
-## Basics
-
-	./docker-brew -h
-
-Display usage and help.
-
-	./docker-brew
-
-Default build from the default repo/branch. Images will be created under the
-`library/` namespace. Does not perform a remote push.
-
-	./docker-brew -n mycorp.com -b stable --push git://github.com/mycorp/docker
-
-Will fetch the library definition files in the `stable` branch of the
-`git://github.com/mycorp/docker` repository and create images under the
-`mycorp.com` namespace (e.g. `mycorp.com/ubuntu`). Created images will then
-be pushed to the official docker repository (pending: support for private
-repositories)
-
-## Library definition files
-
-The library definition files are plain text files found in the `library/`
-subfolder of the docker repository.
-
-### File names
-
-The name of a definition file will determine the name of the image(s) it
-creates. For example, the `library/ubuntu` file will create images in the
-`<namespace>/ubuntu` repository. If multiple instructions are present in
-a single file, all images are expected to be created under a different tag.
-
-### Instruction format
-
-Each line represents a build instruction.
-There are different formats that `docker-brew` is able to parse.
-
-	<git-url>
-	git://github.com/dotcloud/hipache
-	https://github.com/dotcloud/docker.git
-
-The simplest format. `docker-brew` will fetch data from the provided git
-repository from the `HEAD`of its `master` branch. Generated image will be
-tagged as `latest`. Use of this format is discouraged because there is no
-way to ensure stability.
-
-	<docker-tag> <git-url>
-	bleeding-edge git://github.com/dotcloud/docker
-	unstable https://github.com/dotcloud/docker-redis.git
-
-A more advanced format. `docker-brew` will fetch data from the provided git
-repository from the `HEAD`of its `master` branch. Generated image will be
-tagged as `<docker-tag>`. Recommended if we always want to provide a snapshot
-of the latest development. Again, no way to ensure stability.
-
-	<docker-tag>	<git-url>	T:<git-tag>
-	2.4.0 	git://github.com/dotcloud/docker-redis	T:2.4.0
-	<docker-tag>	<git-url>	B:<git-branch>
-	zfs		git://github.com/dotcloud/docker	B:zfs-support
-	<docker-tag>	<git-url>	C:<git-commit-id>
-	2.2.0 	https://github.com/dotcloud/docker-redis.git C:a4bf8923ee4ec566d3ddc212
-
-The most complete format. `docker-brew` will fetch data from the provided git
-repository from the provided reference (if it's a branch, brew will fetch its
-`HEAD`). Generated image will be tagged as `<docker-tag>`. Recommended whenever
-possible.

+ 0 - 1
contrib/brew/brew/__init__.py

@@ -1 +0,0 @@
-from brew import build_library, DEFAULT_REPOSITORY, DEFAULT_BRANCH

+ 0 - 185
contrib/brew/brew/brew.py

@@ -1,185 +0,0 @@
-import os
-import logging
-from shutil import rmtree
-
-import docker
-
-import git
-
-DEFAULT_REPOSITORY = 'git://github.com/dotcloud/docker'
-DEFAULT_BRANCH = 'master'
-
-logger = logging.getLogger(__name__)
-logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
-                    level='INFO')
-client = docker.Client()
-processed = {}
-processed_folders = []
-
-
-def build_library(repository=None, branch=None, namespace=None, push=False,
-        debug=False, prefill=True, registry=None):
-    dst_folder = None
-    summary = Summary()
-    if repository is None:
-        repository = DEFAULT_REPOSITORY
-    if branch is None:
-        branch = DEFAULT_BRANCH
-    if debug:
-        logger.setLevel('DEBUG')
-
-    if not (repository.startswith('https://') or repository.startswith('git://')):
-        logger.info('Repository provided assumed to be a local path')
-        dst_folder = repository
-
-    try:
-        client.version()
-    except Exception as e:
-        logger.error('Could not reach the docker daemon. Please make sure it '
-            'is running.')
-        logger.warning('Also make sure you have access to the docker UNIX '
-            'socket (use sudo)')
-        return
-
-    #FIXME: set destination folder and only pull latest changes instead of
-    # cloning the whole repo everytime
-    if not dst_folder:
-        logger.info('Cloning docker repo from {0}, branch: {1}'.format(
-            repository, branch))
-        try:
-            rep, dst_folder = git.clone_branch(repository, branch)
-        except Exception as e:
-            logger.exception(e)
-            logger.error('Source repository could not be fetched. Check '
-                'that the address is correct and the branch exists.')
-            return
-    try:
-        dirlist = os.listdir(os.path.join(dst_folder, 'library'))
-    except OSError as e:
-        logger.error('The path provided ({0}) could not be found or didn\'t'
-            'contain a library/ folder.'.format(dst_folder))
-        return
-    for buildfile in dirlist:
-        if buildfile == 'MAINTAINERS':
-            continue
-        f = open(os.path.join(dst_folder, 'library', buildfile))
-        linecnt = 0
-        for line in f:
-            linecnt = linecnt + 1
-            logger.debug('{0} ---> {1}'.format(buildfile, line))
-            args = line.split()
-            try:
-                if len(args) > 3:
-                    raise RuntimeError('Incorrect line format, '
-                        'please refer to the docs')
-
-                url = None
-                ref = 'refs/heads/master'
-                tag = None
-                if len(args) == 1:  # Just a URL, simple mode
-                    url = args[0]
-                elif len(args) == 2 or len(args) == 3:  # docker-tag   url
-                    url = args[1]
-                    tag = args[0]
-
-                if len(args) == 3:  # docker-tag  url     B:branch or T:tag
-                    ref = None
-                    if args[2].startswith('B:'):
-                        ref = 'refs/heads/' + args[2][2:]
-                    elif args[2].startswith('T:'):
-                        ref = 'refs/tags/' + args[2][2:]
-                    elif args[2].startswith('C:'):
-                        ref = args[2][2:]
-                    else:
-                        raise RuntimeError('Incorrect line format, '
-                            'please refer to the docs')
-                if prefill:
-                    logger.debug('Pulling {0} from official repository (cache '
-                        'fill)'.format(buildfile))
-                    client.pull(buildfile)
-                img = build_repo(url, ref, buildfile, tag, namespace, push,
-                    registry)
-                summary.add_success(buildfile, (linecnt, line), img)
-                processed['{0}@{1}'.format(url, ref)] = img
-            except Exception as e:
-                logger.exception(e)
-                summary.add_exception(buildfile, (linecnt, line), e)
-
-        f.close()
-    if dst_folder != repository:
-        rmtree(dst_folder, True)
-    for d in processed_folders:
-        rmtree(d, True)
-    summary.print_summary(logger)
-
-
-def build_repo(repository, ref, docker_repo, docker_tag, namespace, push, registry):
-    docker_repo = '{0}/{1}'.format(namespace or 'library', docker_repo)
-    img_id = None
-    dst_folder = None
-    if '{0}@{1}'.format(repository, ref) not in processed.keys():
-        logger.info('Cloning {0} (ref: {1})'.format(repository, ref))
-        if repository not in processed:
-            rep, dst_folder = git.clone(repository, ref)
-            processed[repository] = rep
-            processed_folders.append(dst_folder)
-        else:
-            dst_folder = git.checkout(processed[repository], ref)
-        if not 'Dockerfile' in os.listdir(dst_folder):
-            raise RuntimeError('Dockerfile not found in cloned repository')
-        logger.info('Building using dockerfile...')
-        img_id, logs = client.build(path=dst_folder, quiet=True)
-    else:
-        img_id = processed['{0}@{1}'.format(repository, ref)]
-    logger.info('Committing to {0}:{1}'.format(docker_repo,
-        docker_tag or 'latest'))
-    client.tag(img_id, docker_repo, docker_tag)
-    if push:
-        logger.info('Pushing result to registry {0}'.format(
-            registry or "default"))
-        if registry is not None:
-            docker_repo = '{0}/{1}'.format(registry, docker_repo)
-            logger.info('Also tagging {0}'.format(docker_repo))
-            client.tag(img_id, docker_repo, docker_tag)
-        client.push(docker_repo)
-    return img_id
-
-
-class Summary(object):
-    def __init__(self):
-        self._summary = {}
-        self._has_exc = False
-
-    def _add_data(self, image, linestr, data):
-        if image not in self._summary:
-            self._summary[image] = { linestr: data }
-        else:
-            self._summary[image][linestr] = data
-
-    def add_exception(self, image, line, exc):
-        lineno, linestr = line
-        self._add_data(image, linestr, { 'line': lineno, 'exc': str(exc) })
-        self._has_exc = True
-
-    def add_success(self, image, line, img_id):
-        lineno, linestr = line
-        self._add_data(image, linestr, { 'line': lineno, 'id': img_id })
-
-    def print_summary(self, logger=None):
-        linesep = ''.center(61, '-') + '\n'
-        s = 'BREW BUILD SUMMARY\n' + linesep
-        success = 'OVERALL SUCCESS: {}\n'.format(not self._has_exc)
-        details = linesep
-        for image, lines in self._summary.iteritems():
-            details = details + '{}\n{}'.format(image, linesep)
-            for linestr, data in lines.iteritems():
-                details = details + '{0:2} | {1} | {2:50}\n'.format(
-                    data['line'],
-                    'KO' if 'exc' in data else 'OK',
-                    data['exc'] if 'exc' in data else data['id']
-                )
-            details = details + linesep
-        if logger:
-            logger.info(s + success + details)
-        else:
-            print s, success, details

+ 0 - 63
contrib/brew/brew/git.py

@@ -1,63 +0,0 @@
-import tempfile
-import logging
-
-from dulwich import index
-from dulwich.client import get_transport_and_path
-from dulwich.repo import Repo
-
-logger = logging.getLogger(__name__)
-
-
-def clone_branch(repo_url, branch="master", folder=None):
-    return clone(repo_url, 'refs/heads/' + branch, folder)
-
-
-def clone_tag(repo_url, tag, folder=None):
-    return clone(repo_url, 'refs/tags/' + tag, folder)
-
-
-def checkout(rep, ref=None):
-    is_commit = False
-    if ref is None:
-        ref = 'refs/heads/master'
-    elif not ref.startswith('refs/'):
-        is_commit = True
-    if is_commit:
-        rep['HEAD'] = rep.commit(ref)
-    else:
-        rep['HEAD'] = rep.refs[ref]
-    indexfile = rep.index_path()
-    tree = rep["HEAD"].tree
-    index.build_index_from_tree(rep.path, indexfile, rep.object_store, tree)
-    return rep.path
-
-def clone(repo_url, ref=None, folder=None):
-    is_commit = False
-    if ref is None:
-        ref = 'refs/heads/master'
-    elif not ref.startswith('refs/'):
-        is_commit = True
-    logger.debug("clone repo_url={0}, ref={1}".format(repo_url, ref))
-    if folder is None:
-        folder = tempfile.mkdtemp()
-    logger.debug("folder = {0}".format(folder))
-    rep = Repo.init(folder)
-    client, relative_path = get_transport_and_path(repo_url)
-    logger.debug("client={0}".format(client))
-
-    remote_refs = client.fetch(relative_path, rep)
-    for k, v in remote_refs.iteritems():
-        try:
-            rep.refs.add_if_new(k, v)
-        except:
-            pass
-
-    if is_commit:
-        rep['HEAD'] = rep.commit(ref)
-    else:
-        rep['HEAD'] = remote_refs[ref]
-    indexfile = rep.index_path()
-    tree = rep["HEAD"].tree
-    index.build_index_from_tree(rep.path, indexfile, rep.object_store, tree)
-    logger.debug("done")
-    return rep, folder

+ 0 - 35
contrib/brew/docker-brew

@@ -1,35 +0,0 @@
-#!/usr/bin/env python
-
-import argparse
-import sys
-
-try:
-    import brew
-except ImportError as e:
-    print str(e)
-    print 'Please install the required dependencies first'
-    print 'sudo pip install -r requirements.txt'
-    sys.exit(1)
-
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser('Build the docker standard library')
-    parser.add_argument('--push', action='store_true', default=False,
-        help='Push generated repositories')
-    parser.add_argument('--debug', default=False, action='store_true',
-        help='Enable debugging output')
-    parser.add_argument('--noprefill', default=True, action='store_false',
-        dest='prefill', help='Disable cache prefill')
-    parser.add_argument('-n', metavar='NAMESPACE', default='library',
-        help='Namespace used for generated repositories.'
-        ' Default is library')
-    parser.add_argument('-b', metavar='BRANCH', default=brew.DEFAULT_BRANCH,
-        help='Branch in the repository where the library definition'
-        ' files will be fetched. Default is ' + brew.DEFAULT_BRANCH)
-    parser.add_argument('repository', default=brew.DEFAULT_REPOSITORY,
-        nargs='?', help='git repository containing the library definition'
-        ' files. Default is ' + brew.DEFAULT_REPOSITORY)
-    parser.add_argument('--reg', default=None, help='Registry address to'
-        ' push build results to. Also sets push to true.')
-    args = parser.parse_args()
-    brew.build_library(args.repository, args.b, args.n,
-        args.push or args.reg is not None, args.debug, args.prefill, args.reg)

+ 0 - 2
contrib/brew/requirements.txt

@@ -1,2 +0,0 @@
-dulwich==0.9.0
--e git://github.com/dotcloud/docker-py.git#egg=docker-py

+ 0 - 22
contrib/brew/setup.py

@@ -1,22 +0,0 @@
-#!/usr/bin/env python
-import os
-from setuptools import setup
-
-ROOT_DIR = os.path.dirname(__file__)
-SOURCE_DIR = os.path.join(ROOT_DIR)
-
-test_requirements = []
-setup(
-    name="docker-brew",
-    version='0.0.1',
-    description="-",
-    packages=['dockerbrew'],
-    install_requires=['dulwich', 'docker'] + test_requirements,
-    zip_safe=False,
-    classifiers=['Development Status :: 3 - Alpha',
-                 'Environment :: Other Environment',
-                 'Intended Audience :: Developers',
-                 'Operating System :: OS Independent',
-                 'Programming Language :: Python',
-                 'Topic :: Utilities'],
-    )

+ 27 - 0
contrib/host-integration/Dockerfile.dev

@@ -0,0 +1,27 @@
+#
+# This Dockerfile will create an image that allows to generate upstart and
+# systemd scripts (more to come)
+#
+# docker-version 0.6.2
+#
+
+FROM		ubuntu:12.10
+MAINTAINER	Guillaume J. Charmes <guillaume@dotcloud.com>
+
+RUN		apt-get update && apt-get install -y wget git mercurial
+
+# Install Go
+RUN		wget --no-check-certificate https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz -O go-1.1.2.tar.gz
+RUN		tar -xzvf go-1.1.2.tar.gz && mv /go /goroot
+RUN		mkdir /go
+
+ENV		GOROOT	  /goroot
+ENV		GOPATH	  /go
+ENV		PATH	  $GOROOT/bin:$PATH
+
+RUN		go get github.com/dotcloud/docker && cd /go/src/github.com/dotcloud/docker && git checkout v0.6.3
+ADD		manager.go	/manager/
+RUN		cd /manager && go build -o /usr/bin/manager
+
+ENTRYPOINT	["/usr/bin/manager"]
+

+ 4 - 0
contrib/host-integration/Dockerfile.min

@@ -0,0 +1,4 @@
+FROM		busybox
+MAINTAINER	Guillaume J. Charmes <guillaume@dotcloud.com>
+ADD		manager	  /usr/bin/
+ENTRYPOINT	["/usr/bin/manager"]

+ 130 - 0
contrib/host-integration/manager.go

@@ -0,0 +1,130 @@
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"github.com/dotcloud/docker"
+	"os"
+	"strings"
+	"text/template"
+)
+
+var templates = map[string]string{
+
+	"upstart": `description "{{.description}}"
+author "{{.author}}"
+start on filesystem and started lxc-net and started docker
+stop on runlevel [!2345]
+respawn
+exec /home/vagrant/goroot/bin/docker start -a {{.container_id}}
+`,
+
+	"systemd": `[Unit]
+	Description={{.description}}
+	Author={{.author}}
+	After=docker.service
+
+[Service]
+	Restart=always
+	ExecStart=/usr/bin/docker start -a {{.container_id}}
+	ExecStop=/usr/bin/docker stop -t 2 {{.container_id}}
+
+[Install]
+	WantedBy=local.target
+`,
+}
+
+func main() {
+	// Parse command line for custom options
+	kind := flag.String("t", "upstart", "Type of manager requested")
+	author := flag.String("a", "<none>", "Author of the image")
+	description := flag.String("d", "<none>", "Description of the image")
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, "\nUsage: manager <container id>\n\n")
+		flag.PrintDefaults()
+	}
+	flag.Parse()
+
+	// We require at least the container ID
+	if flag.NArg() != 1 {
+		println(flag.NArg())
+		flag.Usage()
+		return
+	}
+
+	// Check that the requested process manager is supported
+	if _, exists := templates[*kind]; !exists {
+		panic("Unkown script template")
+	}
+
+	// Load the requested template
+	tpl, err := template.New("processManager").Parse(templates[*kind])
+	if err != nil {
+		panic(err)
+	}
+
+	// Create stdout/stderr buffers
+	bufOut := bytes.NewBuffer(nil)
+	bufErr := bytes.NewBuffer(nil)
+
+	// Instanciate the Docker CLI
+	cli := docker.NewDockerCli(nil, bufOut, bufErr, "unix", "/var/run/docker.sock")
+	// Retrieve the container info
+	if err := cli.CmdInspect(flag.Arg(0)); err != nil {
+		// As of docker v0.6.3, CmdInspect always returns nil
+		panic(err)
+	}
+
+	// If there is nothing in the error buffer, then the Docker daemon is there and the container has been found
+	if bufErr.Len() == 0 {
+		// Unmarshall the resulting container data
+		c := []*docker.Container{{}}
+		if err := json.Unmarshal(bufOut.Bytes(), &c); err != nil {
+			panic(err)
+		}
+		// Reset the buffers
+		bufOut.Reset()
+		bufErr.Reset()
+		// Retrieve the info of the linked image
+		if err := cli.CmdInspect(c[0].Image); err != nil {
+			panic(err)
+		}
+		// If there is nothing in the error buffer, then the image has been found.
+		if bufErr.Len() == 0 {
+			// Unmarshall the resulting image data
+			img := []*docker.Image{{}}
+			if err := json.Unmarshal(bufOut.Bytes(), &img); err != nil {
+				panic(err)
+			}
+			// If no author has been set, use the one from the image
+			if *author == "<none>" && img[0].Author != "" {
+				*author = strings.Replace(img[0].Author, "\"", "", -1)
+			}
+			// If no description has been set, use the comment from the image
+			if *description == "<none>" && img[0].Comment != "" {
+				*description = strings.Replace(img[0].Comment, "\"", "", -1)
+			}
+		}
+	}
+
+	/// Old version: Wrtie the resulting script to file
+	// f, err := os.OpenFile(kind, os.O_CREATE|os.O_WRONLY, 0755)
+	// if err != nil {
+	// 	panic(err)
+	// }
+	// defer f.Close()
+
+	// Create a map with needed data
+	data := map[string]string{
+		"author":       *author,
+		"description":  *description,
+		"container_id": flag.Arg(0),
+	}
+
+	// Process the template and output it on Stdout
+	if err := tpl.Execute(os.Stdout, data); err != nil {
+		panic(err)
+	}
+}

+ 53 - 0
contrib/host-integration/manager.sh

@@ -0,0 +1,53 @@
+#!/bin/sh
+set -e
+
+usage() {
+	echo >&2 "usage: $0 [-a author] [-d description] container [manager]"
+	echo >&2 "   ie: $0 -a 'John Smith' 4ec9612a37cd systemd"
+	echo >&2 "   ie: $0 -d 'Super Cool System' 4ec9612a37cd # defaults to upstart"
+	exit 1
+}
+
+auth='<none>'
+desc='<none>'
+have_auth=
+have_desc=
+while getopts a:d: opt; do
+	case "$opt" in
+		a)
+			auth="$OPTARG"
+			have_auth=1
+			;;
+		d)
+			desc="$OPTARG"
+			have_desc=1
+			;;
+	esac
+done
+shift $(($OPTIND - 1))
+
+[ $# -ge 1 -a $# -le 2 ] || usage
+
+cid="$1"
+script="${2:-upstart}"
+if [ ! -e "manager/$script" ]; then
+	echo >&2 "Error: manager type '$script' is unknown (PRs always welcome!)."
+	echo >&2 'The currently supported types are:'
+	echo >&2 "  $(cd manager && echo *)"
+	exit 1
+fi
+
+# TODO https://github.com/dotcloud/docker/issues/734 (docker inspect formatting)
+#if command -v docker > /dev/null 2>&1; then
+#	image="$(docker inspect -f '{{.Image}}' "$cid")"
+#	if [ "$image" ]; then
+#		if [ -z "$have_auth" ]; then
+#			auth="$(docker inspect -f '{{.Author}}' "$image")"
+#		fi
+#		if [ -z "$have_desc" ]; then
+#			desc="$(docker inspect -f '{{.Comment}}' "$image")"
+#		fi
+#	fi
+#fi
+
+exec "manager/$script" "$cid" "$auth" "$desc"

+ 20 - 0
contrib/host-integration/manager/systemd

@@ -0,0 +1,20 @@
+#!/bin/sh
+set -e
+
+cid="$1"
+auth="$2"
+desc="$3"
+
+cat <<-EOF
+	[Unit]
+	Description=$desc
+	Author=$auth
+	After=docker.service
+	
+	[Service]
+	ExecStart=/usr/bin/docker start -a $cid
+	ExecStop=/usr/bin/docker stop -t 2 $cid
+	
+	[Install]
+	WantedBy=local.target
+EOF

+ 15 - 0
contrib/host-integration/manager/upstart

@@ -0,0 +1,15 @@
+#!/bin/sh
+set -e
+
+cid="$1"
+auth="$2"
+desc="$3"
+
+cat <<-EOF
+	description "$(echo "$desc" | sed 's/"/\\"/g')"
+	author "$(echo "$auth" | sed 's/"/\\"/g')"
+	start on filesystem and started lxc-net and started docker
+	stop on runlevel [!2345]
+	respawn
+	exec /usr/bin/docker start -a "$cid"
+EOF

+ 13 - 0
contrib/init/openrc/docker.confd

@@ -0,0 +1,13 @@
+# /etc/conf.d/docker: config file for /etc/init.d/docker
+
+# where the docker daemon output gets piped
+#DOCKER_LOGFILE="/var/log/docker.log"
+
+# where docker's pid get stored
+#DOCKER_PIDFILE="/run/docker.pid"
+
+# where the docker daemon itself is run from
+#DOCKER_BINARY="/usr/bin/docker"
+
+# any other random options you want to pass to docker
+DOCKER_OPTS=""

+ 31 - 0
contrib/init/openrc/docker.initd

@@ -0,0 +1,31 @@
+#!/sbin/runscript
+# Copyright 1999-2013 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+DOCKER_LOGFILE=${DOCKER_LOGFILE:-/var/log/${SVCNAME}.log}
+DOCKER_PIDFILE=${DOCKER_PIDFILE:-/run/${SVCNAME}.pid}
+DOCKER_BINARY=${DOCKER_BINARY:-/usr/bin/docker}
+DOCKER_OPTS=${DOCKER_OPTS:-}
+
+start() {
+	checkpath -f -m 0644 -o root:docker "$DOCKER_LOGFILE"
+
+	ebegin "Starting docker daemon"
+	start-stop-daemon --start --background \
+		--exec "$DOCKER_BINARY" \
+		--pidfile "$DOCKER_PIDFILE" \
+		--stdout "$DOCKER_LOGFILE" \
+		--stderr "$DOCKER_LOGFILE" \
+		-- -d -p "$DOCKER_PIDFILE" \
+		$DOCKER_OPTS
+	eend $?
+}
+
+stop() {
+	ebegin "Stopping docker daemon"
+	start-stop-daemon --stop \
+		--exec "$DOCKER_BINARY" \
+		--pidfile "$DOCKER_PIDFILE"
+	eend $?
+}

+ 13 - 0
contrib/init/systemd/docker.service

@@ -0,0 +1,13 @@
+[Unit]
+Description=Easily create lightweight, portable, self-sufficient containers from any application!
+Documentation=http://docs.docker.io
+Requires=network.target
+After=multi-user.target
+
+[Service]
+Type=simple
+ExecStartPre=/bin/mount --make-rprivate /
+ExecStart=/usr/bin/docker -d
+
+[Install]
+WantedBy=multi-user.target

+ 85 - 0
contrib/init/sysvinit/docker

@@ -0,0 +1,85 @@
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides:           docker
+# Required-Start:     $syslog $remote_fs
+# Required-Stop:      $syslog $remote_fs
+# Default-Start:      2 3 4 5
+# Default-Stop:       0 1 6
+# Short-Description:  Linux container runtime
+# Description:        Linux container runtime
+### END INIT INFO
+
+DOCKER=/usr/bin/docker
+DOCKER_PIDFILE=/var/run/docker.pid
+DOCKER_OPTS=
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
+
+# Check lxc-docker is present
+[ -x $DOCKER ] || (log_failure_msg "docker not present"; exit 1)
+
+# Get lsb functions
+. /lib/lsb/init-functions
+
+if [ -f /etc/default/lxc ]; then
+	. /etc/default/lxc
+fi
+
+if [ "$1" = start ] && which initctl >/dev/null && initctl version | grep -q upstart; then
+	exit 1
+fi
+
+check_root_id ()
+{
+	if [ "$(id -u)" != "0" ]; then
+		log_failure_msg "Docker must be run as root"; exit 1
+	fi
+}
+
+case "$1" in
+	start)
+		check_root_id || exit 1
+		log_begin_msg "Starting Docker"
+		mount | grep cgroup >/dev/null || mount -t cgroup none /sys/fs/cgroup 2>/dev/null
+		start-stop-daemon --start --background $NO_CLOSE \
+			--exec "$DOCKER" \
+			--pidfile "$DOCKER_PIDFILE" \
+			-- -d -p "$DOCKER_PIDFILE" \
+			$DOCKER_OPTS
+		log_end_msg $?
+		;;
+
+	stop)
+		check_root_id || exit 1
+		log_begin_msg "Stopping Docker"
+		start-stop-daemon --stop \
+			--pidfile "$DOCKER_PIDFILE"
+		log_end_msg $?
+		;;
+
+	restart)
+		check_root_id || exit 1
+		docker_pid=`cat "$DOCKER_PIDFILE" 2>/dev/null`
+		[ -n "$docker_pid" ] \
+			&& ps -p $docker_pid > /dev/null 2>&1 \
+			&& $0 stop
+		$0 start
+		;;
+
+	force-reload)
+		check_root_id || exit 1
+		$0 restart
+		;;
+
+	status)
+		status_of_proc -p "$DOCKER_PIDFILE" "$DOCKER" docker
+		;;
+
+	*)
+		echo "Usage: $0 {start|stop|restart|status}"
+		exit 1
+		;;
+esac
+
+exit 0

+ 10 - 0
contrib/init/upstart/docker.conf

@@ -0,0 +1,10 @@
+description "Docker daemon"
+
+start on filesystem and started lxc-net
+stop on runlevel [!2345]
+
+respawn
+
+script
+	/usr/bin/docker -d
+end script

+ 12 - 10
contrib/mkimage-arch.sh

@@ -21,18 +21,20 @@ mkdir $ROOTFS
 #packages to ignore for space savings
 #packages to ignore for space savings
 PKGIGNORE=linux,jfsutils,lvm2,cryptsetup,groff,man-db,man-pages,mdadm,pciutils,pcmciautils,reiserfsprogs,s-nail,xfsprogs
 PKGIGNORE=linux,jfsutils,lvm2,cryptsetup,groff,man-db,man-pages,mdadm,pciutils,pcmciautils,reiserfsprogs,s-nail,xfsprogs
  
  
-expect -c "
+expect <<EOF
   set timeout 60
   set timeout 60
+  set send_slow {1 1}
   spawn pacstrap -c -d -G -i $ROOTFS base haveged --ignore $PKGIGNORE
   spawn pacstrap -c -d -G -i $ROOTFS base haveged --ignore $PKGIGNORE
   expect {
   expect {
-    \"Install anyway\" { send "n\\r"; exp_continue }
-    \"(default=all)\" { send "\\r"; exp_continue }
-    \"Proceed with installation?\" { send "\\r"; exp_continue }
-    \"skip the above package\" {send "y\\r"; exp_continue }
-    \"checking\" { exp_continue }
-    \"loading\" { exp_continue }
-    \"installing\" { exp_continue }
-  }"
+    "Install anyway?" { send n\r; exp_continue }
+    "(default=all)" { send \r; exp_continue }
+    "Proceed with installation?" { send "\r"; exp_continue }
+    "skip the above package" {send "y\r"; exp_continue }
+    "checking" { exp_continue }
+    "loading" { exp_continue }
+    "installing" { exp_continue }
+  }
+EOF
 
 
 arch-chroot $ROOTFS /bin/sh -c "haveged -w 1024; pacman-key --init; pkill haveged; pacman -Rs --noconfirm haveged; pacman-key --populate archlinux"
 arch-chroot $ROOTFS /bin/sh -c "haveged -w 1024; pacman-key --init; pkill haveged; pacman -Rs --noconfirm haveged; pacman-key --populate archlinux"
 arch-chroot $ROOTFS /bin/sh -c "ln -s /usr/share/zoneinfo/UTC /etc/localtime"
 arch-chroot $ROOTFS /bin/sh -c "ln -s /usr/share/zoneinfo/UTC /etc/localtime"
@@ -60,6 +62,6 @@ mknod -m 666 ${DEV}/full c 1 7
 mknod -m 600 ${DEV}/initctl p
 mknod -m 600 ${DEV}/initctl p
 mknod -m 666 ${DEV}/ptmx c 5 2
 mknod -m 666 ${DEV}/ptmx c 5 2
 
 
-tar -C $ROOTFS -c . | docker import - archlinux
+tar --numeric-owner -C $ROOTFS -c . | docker import - archlinux
 docker run -i -t archlinux echo Success.
 docker run -i -t archlinux echo Success.
 rm -rf $ROOTFS
 rm -rf $ROOTFS

+ 1 - 1
contrib/mkimage-busybox.sh

@@ -35,5 +35,5 @@ do
     cp -a /dev/$X dev
     cp -a /dev/$X dev
 done
 done
 
 
-tar -cf- . | docker import - busybox
+tar --numeric-owner -cf- . | docker import - busybox
 docker run -i -u root busybox /bin/echo Success.
 docker run -i -u root busybox /bin/echo Success.

+ 0 - 83
contrib/mkimage-debian.sh

@@ -1,83 +0,0 @@
-#!/bin/bash
-set -e
-
-# these should match the names found at http://www.debian.org/releases/
-stableSuite='wheezy'
-testingSuite='jessie'
-unstableSuite='sid'
-
-variant='minbase'
-include='iproute,iputils-ping'
-
-repo="$1"
-suite="$2"
-mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
-
-if [ ! "$repo" ] || [ ! "$suite" ]; then
-	echo >&2 "usage: $0 repo suite [mirror]"
-	echo >&2
-	echo >&2 "   ie: $0 tianon/debian squeeze"
-	echo >&2 "       $0 tianon/debian squeeze http://ftp.uk.debian.org/debian/"
-	echo >&2
-	echo >&2 "   ie: $0 tianon/ubuntu precise"
-	echo >&2 "       $0 tianon/ubuntu precise http://mirrors.melbourne.co.uk/ubuntu/"
-	echo >&2
-	exit 1
-fi
-
-target="/tmp/docker-rootfs-debian-$suite-$$-$RANDOM"
-
-cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
-returnTo="$(pwd -P)"
-
-set -x
-
-# bootstrap
-mkdir -p "$target"
-sudo debootstrap --verbose --variant="$variant" --include="$include" "$suite" "$target" "$mirror"
-
-cd "$target"
-
-# prevent init scripts from running during install/update
-#  policy-rc.d (for most scripts)
-echo $'#!/bin/sh\nexit 101' | sudo tee usr/sbin/policy-rc.d > /dev/null
-sudo chmod +x usr/sbin/policy-rc.d
-#  initctl (for some pesky upstart scripts)
-sudo chroot . dpkg-divert --local --rename --add /sbin/initctl
-sudo ln -sf /bin/true sbin/initctl
-# see https://github.com/dotcloud/docker/issues/446#issuecomment-16953173
-
-# shrink the image, since apt makes us fat (wheezy: ~157.5MB vs ~120MB)
-sudo chroot . apt-get clean
-
-# while we're at it, apt is unnecessarily slow inside containers
-#  this forces dpkg not to call sync() after package extraction and speeds up install
-#    the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization
-echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null
-#  we want to effectively run "apt-get clean" after every install to keep images small
-echo 'DPkg::Post-Invoke {"/bin/rm -f /var/cache/apt/archives/*.deb || true";};' | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
-
-# helpful undo lines for each the above tweaks (for lack of a better home to keep track of them):
-#  rm /usr/sbin/policy-rc.d
-#  rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl
-#  rm /etc/dpkg/dpkg.cfg.d/02apt-speedup
-#  rm /etc/apt/apt.conf.d/no-cache
-
-# create the image (and tag $repo:$suite)
-sudo tar -c . | docker import - $repo $suite
-
-# test the image
-docker run -i -t $repo:$suite echo success
-
-if [ "$suite" = "$stableSuite" -o "$suite" = 'stable' ]; then
-	# tag latest
-	docker tag $repo:$suite $repo latest
-	
-	# tag the specific debian release version
-	ver=$(docker run $repo:$suite cat /etc/debian_version)
-	docker tag $repo:$suite $repo $ver
-fi
-
-# cleanup
-cd "$returnTo"
-sudo rm -rf "$target"

+ 233 - 0
contrib/mkimage-debootstrap.sh

@@ -0,0 +1,233 @@
+#!/bin/bash
+set -e
+
+variant='minbase'
+include='iproute,iputils-ping'
+arch='amd64' # intentionally undocumented for now
+skipDetection=
+strictDebootstrap=
+justTar=
+
+usage() {
+	echo >&2
+	
+	echo >&2 "usage: $0 [options] repo suite [mirror]"
+	
+	echo >&2
+	echo >&2 'options: (not recommended)'
+	echo >&2 "  -p set an http_proxy for debootstrap"
+	echo >&2 "  -v $variant # change default debootstrap variant"
+	echo >&2 "  -i $include # change default package includes"
+	echo >&2 "  -d # strict debootstrap (do not apply any docker-specific tweaks)"
+	echo >&2 "  -s # skip version detection and tagging (ie, precise also tagged as 12.04)"
+	echo >&2 "     # note that this will also skip adding universe and/or security/updates to sources.list"
+	echo >&2 "  -t # just create a tarball, especially for dockerbrew (uses repo as tarball name)"
+	
+	echo >&2
+	echo >&2 "   ie: $0 username/debian squeeze"
+	echo >&2 "       $0 username/debian squeeze http://ftp.uk.debian.org/debian/"
+	
+	echo >&2
+	echo >&2 "   ie: $0 username/ubuntu precise"
+	echo >&2 "       $0 username/ubuntu precise http://mirrors.melbourne.co.uk/ubuntu/"
+	
+	echo >&2
+	echo >&2 "   ie: $0 -t precise.tar.bz2 precise"
+	echo >&2 "       $0 -t wheezy.tgz wheezy"
+	echo >&2 "       $0 -t wheezy-uk.tar.xz wheezy http://ftp.uk.debian.org/debian/"
+	
+	echo >&2
+}
+
+# these should match the names found at http://www.debian.org/releases/
+debianStable=wheezy
+debianUnstable=sid
+# this should match the name found at http://releases.ubuntu.com/
+ubuntuLatestLTS=precise
+
+while getopts v:i:a:p:dst name; do
+	case "$name" in
+		p)
+			http_proxy="$OPTARG"
+			;;
+		v)
+			variant="$OPTARG"
+			;;
+		i)
+			include="$OPTARG"
+			;;
+		a)
+			arch="$OPTARG"
+			;;
+		d)
+			strictDebootstrap=1
+			;;
+		s)
+			skipDetection=1
+			;;
+		t)
+			justTar=1
+			;;
+		?)
+			usage
+			exit 0
+			;;
+	esac
+done
+shift $(($OPTIND - 1))
+
+repo="$1"
+suite="$2"
+mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
+
+if [ ! "$repo" ] || [ ! "$suite" ]; then
+	usage
+	exit 1
+fi
+
+# some rudimentary detection for whether we need to "sudo" our docker calls
+docker=''
+if docker version > /dev/null 2>&1; then
+	docker='docker'
+elif sudo docker version > /dev/null 2>&1; then
+	docker='sudo docker'
+elif command -v docker > /dev/null 2>&1; then
+	docker='docker'
+else
+	echo >&2 "warning: either docker isn't installed, or your current user cannot run it;"
+	echo >&2 "         this script is not likely to work as expected"
+	sleep 3
+	docker='docker' # give us a command-not-found later
+fi
+
+# make sure we have an absolute path to our final tarball so we can still reference it properly after we change directory
+if [ "$justTar" ]; then
+	if [ ! -d "$(dirname "$repo")" ]; then
+		echo >&2 "error: $(dirname "$repo") does not exist"
+		exit 1
+	fi
+	repo="$(cd "$(dirname "$repo")" && pwd -P)/$(basename "$repo")"
+fi
+
+# will be filled in later, if [ -z "$skipDetection" ]
+lsbDist=''
+
+target="/tmp/docker-rootfs-debootstrap-$suite-$$-$RANDOM"
+
+cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
+returnTo="$(pwd -P)"
+
+set -x
+
+# bootstrap
+mkdir -p "$target"
+sudo http_proxy=$http_proxy debootstrap --verbose --variant="$variant" --include="$include" --arch="$arch" "$suite" "$target" "$mirror"
+
+cd "$target"
+
+if [ -z "$strictDebootstrap" ]; then
+	# prevent init scripts from running during install/update
+	#  policy-rc.d (for most scripts)
+	echo $'#!/bin/sh\nexit 101' | sudo tee usr/sbin/policy-rc.d > /dev/null
+	sudo chmod +x usr/sbin/policy-rc.d
+	#  initctl (for some pesky upstart scripts)
+	sudo chroot . dpkg-divert --local --rename --add /sbin/initctl
+	sudo ln -sf /bin/true sbin/initctl
+	# see https://github.com/dotcloud/docker/issues/446#issuecomment-16953173
+	
+	# shrink the image, since apt makes us fat (wheezy: ~157.5MB vs ~120MB)
+	sudo chroot . apt-get clean
+	
+	# while we're at it, apt is unnecessarily slow inside containers
+	#  this forces dpkg not to call sync() after package extraction and speeds up install
+	#    the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization
+	echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null
+	#  we want to effectively run "apt-get clean" after every install to keep images small
+	echo 'DPkg::Post-Invoke {"/bin/rm -f /var/cache/apt/archives/*.deb || true";};' | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
+	
+	# helpful undo lines for each the above tweaks (for lack of a better home to keep track of them):
+	#  rm /usr/sbin/policy-rc.d
+	#  rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl
+	#  rm /etc/dpkg/dpkg.cfg.d/02apt-speedup
+	#  rm /etc/apt/apt.conf.d/no-cache
+	
+	if [ -z "$skipDetection" ]; then
+		# see also rudimentary platform detection in hack/install.sh
+		lsbDist=''
+		if [ -r etc/lsb-release ]; then
+			lsbDist="$(. etc/lsb-release && echo "$DISTRIB_ID")"
+		fi
+		if [ -z "$lsbDist" ] && [ -r etc/debian_version ]; then
+			lsbDist='Debian'
+		fi
+		
+		case "$lsbDist" in
+			Debian)
+				# add the updates and security repositories
+				if [ "$suite" != "$debianUnstable" -a "$suite" != 'unstable' ]; then
+					# ${suite}-updates only applies to non-unstable
+					sudo sed -i "p; s/ $suite main$/ ${suite}-updates main/" etc/apt/sources.list
+					
+					# same for security updates
+					echo "deb http://security.debian.org/ $suite/updates main" | sudo tee -a etc/apt/sources.list > /dev/null
+				fi
+				;;
+			Ubuntu)
+				# add the universe, updates, and security repositories
+				sudo sed -i "
+					s/ $suite main$/ $suite main universe/; p;
+					s/ $suite main/ ${suite}-updates main/; p;
+					s/ $suite-updates main/ ${suite}-security main/
+				" etc/apt/sources.list
+				;;
+		esac
+	fi
+fi
+
+if [ "$justTar" ]; then
+	# create the tarball file so it has the right permissions (ie, not root)
+	touch "$repo"
+	
+	# fill the tarball
+	sudo tar --numeric-owner -caf "$repo" .
+else
+	# create the image (and tag $repo:$suite)
+	sudo tar --numeric-owner -c . | $docker import - $repo $suite
+	
+	# test the image
+	$docker run -i -t $repo:$suite echo success
+	
+	if [ -z "$skipDetection" ]; then
+		case "$lsbDist" in
+			Debian)
+				if [ "$suite" = "$debianStable" -o "$suite" = 'stable' ] && [ -r etc/debian_version ]; then
+					# tag latest
+					$docker tag $repo:$suite $repo latest
+					
+					if [ -r etc/debian_version ]; then
+						# tag the specific debian release version (which is only reasonable to tag on debian stable)
+						ver=$(cat etc/debian_version)
+						$docker tag $repo:$suite $repo $ver
+					fi
+				fi
+				;;
+			Ubuntu)
+				if [ "$suite" = "$ubuntuLatestLTS" ]; then
+					# tag latest
+					$docker tag $repo:$suite $repo latest
+				fi
+				if [ -r etc/lsb-release ]; then
+					lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
+					if [ "$lsbRelease" ]; then
+						# tag specific Ubuntu version number, if available (12.04, etc.)
+						$docker tag $repo:$suite $repo $lsbRelease
+					fi
+				fi
+				;;
+		esac
+	fi
+fi
+
+# cleanup
+cd "$returnTo"
+sudo rm -rf "$target"

+ 1 - 1
contrib/mkimage-unittest.sh

@@ -44,6 +44,6 @@ do
 done
 done
 
 
 chmod 0755 $ROOTFS # See #486
 chmod 0755 $ROOTFS # See #486
-tar -cf- . | docker import - docker-ut
+tar --numeric-owner -cf- . | docker import - docker-ut
 docker run -i -u root docker-ut /bin/echo Success.
 docker run -i -u root docker-ut /bin/echo Success.
 rm -rf $ROOTFS
 rm -rf $ROOTFS

+ 2 - 0
contrib/vim-syntax/syntax/dockerfile.vim

@@ -20,3 +20,5 @@ highlight link dockerfileString String
 
 
 syntax match dockerfileComment "\v^\s*#.*$"
 syntax match dockerfileComment "\v^\s*#.*$"
 highlight link dockerfileComment Comment
 highlight link dockerfileComment Comment
+
+set commentstring=#\ %s

+ 54 - 26
docker/docker.go

@@ -4,9 +4,11 @@ import (
 	"flag"
 	"flag"
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker"
+	"github.com/dotcloud/docker/sysinit"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
+	"net"
 	"os"
 	"os"
 	"os/signal"
 	"os/signal"
 	"strconv"
 	"strconv"
@@ -22,7 +24,7 @@ var (
 func main() {
 func main() {
 	if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" {
 	if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" {
 		// Running in init mode
 		// Running in init mode
-		docker.SysInit()
+		sysinit.SysInit()
 		return
 		return
 	}
 	}
 	// FIXME: Switch d and D ? (to be more sshd like)
 	// FIXME: Switch d and D ? (to be more sshd like)
@@ -35,9 +37,14 @@ func main() {
 	flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.")
 	flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.")
 	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
 	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
 	flDns := flag.String("dns", "", "Set custom dns servers")
 	flDns := flag.String("dns", "", "Set custom dns servers")
-	flHosts := docker.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
+	flHosts := utils.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
 	flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use")
 	flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use")
+	flEnableIptables := flag.Bool("iptables", true, "Disable iptables within docker")
+	flDefaultIp := flag.String("ip", "0.0.0.0", "Default ip address to use when binding a containers ports")
+	flInterContainerComm := flag.Bool("enable-container-comm", false, "Enable inter-container communication")
+
 	flag.Parse()
 	flag.Parse()
+
 	if *flVersion {
 	if *flVersion {
 		showVersion()
 		showVersion()
 		return
 		return
@@ -46,13 +53,17 @@ func main() {
 		flHosts = flHosts[1:] //trick to display a nice default value in the usage
 		flHosts = flHosts[1:] //trick to display a nice default value in the usage
 	}
 	}
 	for i, flHost := range flHosts {
 	for i, flHost := range flHosts {
-		flHosts[i] = utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost)
+		host, err := utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost)
+		if err == nil {
+			flHosts[i] = host
+		} else {
+			log.Fatal(err)
+		}
 	}
 	}
 
 
+	bridge := docker.DefaultNetworkBridge
 	if *bridgeName != "" {
 	if *bridgeName != "" {
-		docker.NetworkBridgeIface = *bridgeName
-	} else {
-		docker.NetworkBridgeIface = docker.DefaultNetworkBridge
+		bridge = *bridgeName
 	}
 	}
 	if *flDebug {
 	if *flDebug {
 		os.Setenv("DEBUG", "1")
 		os.Setenv("DEBUG", "1")
@@ -64,14 +75,31 @@ func main() {
 			flag.Usage()
 			flag.Usage()
 			return
 			return
 		}
 		}
-		if err := daemon(*pidfile, *flGraphPath, flHosts, *flAutoRestart, *flEnableCors, *flDns); err != nil {
+		var dns []string
+		if *flDns != "" {
+			dns = []string{*flDns}
+		}
+
+		ip := net.ParseIP(*flDefaultIp)
+
+		config := &docker.DaemonConfig{
+			Pidfile:                     *pidfile,
+			GraphPath:                   *flGraphPath,
+			AutoRestart:                 *flAutoRestart,
+			EnableCors:                  *flEnableCors,
+			Dns:                         dns,
+			EnableIptables:              *flEnableIptables,
+			BridgeIface:                 bridge,
+			ProtoAddresses:              flHosts,
+			DefaultIp:                   ip,
+			InterContainerCommunication: *flInterContainerComm,
+		}
+		if err := daemon(config); err != nil {
 			log.Fatal(err)
 			log.Fatal(err)
-			os.Exit(-1)
 		}
 		}
 	} else {
 	} else {
 		if len(flHosts) > 1 {
 		if len(flHosts) > 1 {
 			log.Fatal("Please specify only one -H")
 			log.Fatal("Please specify only one -H")
-			return
 		}
 		}
 		protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
 		protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
 		if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
 		if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
@@ -79,7 +107,6 @@ func main() {
 				os.Exit(sterr.Status)
 				os.Exit(sterr.Status)
 			}
 			}
 			log.Fatal(err)
 			log.Fatal(err)
-			os.Exit(-1)
 		}
 		}
 	}
 	}
 }
 }
@@ -115,30 +142,30 @@ func removePidFile(pidfile string) {
 	}
 	}
 }
 }
 
 
-func daemon(pidfile string, flGraphPath string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error {
-	if err := createPidFile(pidfile); err != nil {
+func daemon(config *docker.DaemonConfig) error {
+	if err := createPidFile(config.Pidfile); err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}
-	defer removePidFile(pidfile)
+	defer removePidFile(config.Pidfile)
+
+	server, err := docker.NewServer(config)
+	if err != nil {
+		return err
+	}
+	defer server.Close()
 
 
 	c := make(chan os.Signal, 1)
 	c := make(chan os.Signal, 1)
 	signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
 	signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
 	go func() {
 	go func() {
 		sig := <-c
 		sig := <-c
 		log.Printf("Received signal '%v', exiting\n", sig)
 		log.Printf("Received signal '%v', exiting\n", sig)
-		removePidFile(pidfile)
+		server.Close()
+		removePidFile(config.Pidfile)
 		os.Exit(0)
 		os.Exit(0)
 	}()
 	}()
-	var dns []string
-	if flDns != "" {
-		dns = []string{flDns}
-	}
-	server, err := docker.NewServer(flGraphPath, autoRestart, enableCors, dns)
-	if err != nil {
-		return err
-	}
-	chErrors := make(chan error, len(protoAddrs))
-	for _, protoAddr := range protoAddrs {
+
+	chErrors := make(chan error, len(config.ProtoAddresses))
+	for _, protoAddr := range config.ProtoAddresses {
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
 		if protoAddrParts[0] == "unix" {
 		if protoAddrParts[0] == "unix" {
 			syscall.Unlink(protoAddrParts[1])
 			syscall.Unlink(protoAddrParts[1])
@@ -147,14 +174,15 @@ func daemon(pidfile string, flGraphPath string, protoAddrs []string, autoRestart
 				log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
 				log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
 			}
 			}
 		} else {
 		} else {
+			server.Close()
+			removePidFile(config.Pidfile)
 			log.Fatal("Invalid protocol format.")
 			log.Fatal("Invalid protocol format.")
-			os.Exit(-1)
 		}
 		}
 		go func() {
 		go func() {
 			chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true)
 			chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true)
 		}()
 		}()
 	}
 	}
-	for i := 0; i < len(protoAddrs); i += 1 {
+	for i := 0; i < len(config.ProtoAddresses); i += 1 {
 		err := <-chErrors
 		err := <-chErrors
 		if err != nil {
 		if err != nil {
 			return err
 			return err

+ 16 - 0
dockerinit/dockerinit.go

@@ -0,0 +1,16 @@
+package main
+
+import (
+	"github.com/dotcloud/docker/sysinit"
+)
+
+var (
+	GITCOMMIT string
+	VERSION   string
+)
+
+func main() {
+	// Running in init mode
+	sysinit.SysInit()
+	return
+}

+ 6 - 1
docs/Dockerfile

@@ -1,11 +1,16 @@
 from ubuntu:12.04
 from ubuntu:12.04
 maintainer Nick Stinemates
 maintainer Nick Stinemates
+#
+#    docker build -t docker:docs . && docker run -p 8000:8000 docker:docs
+#
 
 
 run apt-get update
 run apt-get update
 run apt-get install -y python-setuptools make
 run apt-get install -y python-setuptools make
 run easy_install pip
 run easy_install pip
+#from docs/requirements.txt, but here to increase cacheability
+run pip install Sphinx==1.1.3
+run pip install sphinxcontrib-httpdomain==1.1.8
 add . /docs
 add . /docs
-run pip install -r /docs/requirements.txt
 run cd /docs; make docs
 run cd /docs; make docs
 
 
 expose 8000
 expose 8000

+ 5 - 4
docs/README.md

@@ -39,8 +39,7 @@ Getting Started
 To edit and test the docs, you'll need to install the Sphinx tool and
 To edit and test the docs, you'll need to install the Sphinx tool and
 its dependencies. There are two main ways to install this tool:
 its dependencies. There are two main ways to install this tool:
 
 
-Native Installation
-...................
+###Native Installation
 
 
 * Install sphinx: `pip install sphinx`
 * Install sphinx: `pip install sphinx`
     * Mac OS X: `[sudo] pip-2.7 install sphinx`
     * Mac OS X: `[sudo] pip-2.7 install sphinx`
@@ -48,8 +47,7 @@ Native Installation
     * Mac OS X: `[sudo] pip-2.7 install sphinxcontrib-httpdomain`
     * Mac OS X: `[sudo] pip-2.7 install sphinxcontrib-httpdomain`
 * If pip is not available you can probably install it using your favorite package manager as **python-pip**
 * If pip is not available you can probably install it using your favorite package manager as **python-pip**
 
 
-Alternative Installation: Docker Container
-..........................................
+###Alternative Installation: Docker Container
 
 
 If you're running ``docker`` on your development machine then you may
 If you're running ``docker`` on your development machine then you may
 find it easier and cleaner to use the Dockerfile. This installs Sphinx
 find it easier and cleaner to use the Dockerfile. This installs Sphinx
@@ -59,6 +57,9 @@ docs inside the container, even starting a simple HTTP server on port
 build .`` and run the resulting image. This is the equivalent to
 build .`` and run the resulting image. This is the equivalent to
 ``make clean server`` since each container starts clean.
 ``make clean server`` since each container starts clean.
 
 
+In the ``docs/`` directory, run:
+    ```docker build -t docker:docs . && docker run -p 8000:8000 docker:docs```
+
 Usage
 Usage
 -----
 -----
 * Follow the contribution guidelines (``../CONTRIBUTING.md``)
 * Follow the contribution guidelines (``../CONTRIBUTING.md``)

+ 21 - 16
docs/sources/api/docker_remote_api_v1.0.rst

@@ -970,28 +970,33 @@ Create a new image from a container's changes
 
 
 	**Example request**:
 	**Example request**:
 
 
-        .. sourcecode:: http
+    .. sourcecode:: http
 
 
-           POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       Content-Type: application/json
+   
+       {
+           "Cmd": ["cat", "/world"],
+           "PortSpecs":["22"]
+       }
 
 
-        **Example response**:
+    **Example response**:
 
 
-        .. sourcecode:: http
+    .. sourcecode:: http
 
 
-           HTTP/1.1 201 OK
-	   Content-Type: application/vnd.docker.raw-stream
+       HTTP/1.1 201 OK
+       Content-Type: application/vnd.docker.raw-stream
 
 
-           {"Id":"596069db4bf5"}
+       {"Id":"596069db4bf5"}
 
 
-	:query container: source container
-	:query repo: repository
-	:query tag: tag
-	:query m: commit message
-	:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
-	:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
-        :statuscode 201: no error
-	:statuscode 404: no such container
-        :statuscode 500: server error
+    :query container: source container
+    :query repo: repository
+    :query tag: tag
+    :query m: commit message
+    :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
+    :statuscode 201: no error
+    :statuscode 404: no such container
+    :statuscode 500: server error
 
 
 
 
 3. Going further
 3. Going further

+ 31 - 26
docs/sources/api/docker_remote_api_v1.1.rst

@@ -977,32 +977,37 @@ Create a new image from a container's changes
 
 
 .. http:post:: /commit
 .. http:post:: /commit
 
 
-	Create a new image from a container's changes
-
-	**Example request**:
-
-        .. sourcecode:: http
-
-           POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
-
-        **Example response**:
-
-        .. sourcecode:: http
-
-           HTTP/1.1 201 OK
-	   Content-Type: application/vnd.docker.raw-stream
-
-           {"Id":"596069db4bf5"}
-
-	:query container: source container
-	:query repo: repository
-	:query tag: tag
-	:query m: commit message
-	:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
-	:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
-        :statuscode 201: no error
-	:statuscode 404: no such container
-        :statuscode 500: server error
+    Create a new image from a container's changes
+
+    **Example request**:
+    
+    .. sourcecode:: http
+    
+       POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       Content-Type: application/json
+   
+       {
+           "Cmd": ["cat", "/world"],
+           "PortSpecs":["22"]
+       }
+    
+    **Example response**:
+    
+    .. sourcecode:: http
+    
+        HTTP/1.1 201 OK
+        Content-Type: application/vnd.docker.raw-stream
+
+        {"Id":"596069db4bf5"}
+
+    :query container: source container
+    :query repo: repository
+    :query tag: tag
+    :query m: commit message
+    :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
+    :statuscode 201: no error
+    :statuscode 404: no such container
+    :statuscode 500: server error
 
 
 
 
 3. Going further
 3. Going further

+ 22 - 17
docs/sources/api/docker_remote_api_v1.2.rst

@@ -985,32 +985,37 @@ Create a new image from a container's changes
 
 
 .. http:post:: /commit
 .. http:post:: /commit
 
 
-	Create a new image from a container's changes
+    Create a new image from a container's changes
 
 
-	**Example request**:
+    **Example request**:
 
 
-        .. sourcecode:: http
+    .. sourcecode:: http
 
 
-           POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       Content-Type: application/json
+   
+       {
+           "Cmd": ["cat", "/world"],
+           "PortSpecs":["22"]
+       }
 
 
-        **Example response**:
+    **Example response**:
 
 
-        .. sourcecode:: http
+    .. sourcecode:: http
 
 
-           HTTP/1.1 201 OK
+       HTTP/1.1 201 OK
 	   Content-Type: application/vnd.docker.raw-stream
 	   Content-Type: application/vnd.docker.raw-stream
 
 
-           {"Id":"596069db4bf5"}
+       {"Id":"596069db4bf5"}
 
 
-	:query container: source container
-	:query repo: repository
-	:query tag: tag
-	:query m: commit message
-	:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
-	:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
-        :statuscode 201: no error
-	:statuscode 404: no such container
-        :statuscode 500: server error
+    :query container: source container
+    :query repo: repository
+    :query tag: tag
+    :query m: commit message
+    :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
+    :statuscode 201: no error
+    :statuscode 404: no such container
+    :statuscode 500: server error
 
 
 
 
 3. Going further
 3. Going further

+ 22 - 17
docs/sources/api/docker_remote_api_v1.3.rst

@@ -1034,32 +1034,37 @@ Create a new image from a container's changes
 
 
 .. http:post:: /commit
 .. http:post:: /commit
 
 
-	Create a new image from a container's changes
+    Create a new image from a container's changes
 
 
-	**Example request**:
+    **Example request**:
 
 
-        .. sourcecode:: http
+    .. sourcecode:: http
 
 
-           POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       Content-Type: application/json
+   
+       {
+           "Cmd": ["cat", "/world"],
+           "PortSpecs":["22"]
+       }
 
 
-        **Example response**:
+    **Example response**:
 
 
-        .. sourcecode:: http
+    .. sourcecode:: http
 
 
-           HTTP/1.1 201 OK
+       HTTP/1.1 201 OK
 	   Content-Type: application/vnd.docker.raw-stream
 	   Content-Type: application/vnd.docker.raw-stream
 
 
-           {"Id":"596069db4bf5"}
+       {"Id":"596069db4bf5"}
 
 
-	:query container: source container
-	:query repo: repository
-	:query tag: tag
-	:query m: commit message
-	:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
-	:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
-        :statuscode 201: no error
-	:statuscode 404: no such container
-        :statuscode 500: server error
+    :query container: source container
+    :query repo: repository
+    :query tag: tag
+    :query m: commit message
+    :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
+    :statuscode 201: no error
+    :statuscode 404: no such container
+    :statuscode 500: server error
 
 
 
 
 Monitor Docker's events
 Monitor Docker's events

+ 10 - 5
docs/sources/api/docker_remote_api_v1.4.rst

@@ -1084,23 +1084,28 @@ Create a new image from a container's changes
 
 
     .. sourcecode:: http
     .. sourcecode:: http
 
 
-        POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       Content-Type: application/json
+   
+       {
+           "Cmd": ["cat", "/world"],
+           "PortSpecs":["22"]
+       }
 
 
     **Example response**:
     **Example response**:
 
 
     .. sourcecode:: http
     .. sourcecode:: http
 
 
-        HTTP/1.1 201 OK
-	    Content-Type: application/vnd.docker.raw-stream
+       HTTP/1.1 201 OK
+	   Content-Type: application/vnd.docker.raw-stream
 
 
-        {"Id":"596069db4bf5"}
+       {"Id":"596069db4bf5"}
 
 
     :query container: source container
     :query container: source container
     :query repo: repository
     :query repo: repository
     :query tag: tag
     :query tag: tag
     :query m: commit message
     :query m: commit message
     :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
     :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
-    :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
     :statuscode 201: no error
     :statuscode 201: no error
     :statuscode 404: no such container
     :statuscode 404: no such container
     :statuscode 500: server error
     :statuscode 500: server error

+ 23 - 18
docs/sources/api/docker_remote_api_v1.5.rst

@@ -1050,32 +1050,37 @@ Create a new image from a container's changes
 
 
 .. http:post:: /commit
 .. http:post:: /commit
 
 
-  Create a new image from a container's changes
+    Create a new image from a container's changes
 
 
-  **Example request**:
+    **Example request**:
 
 
-  .. sourcecode:: http
+    .. sourcecode:: http
 
 
-    POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       Content-Type: application/json
+   
+       {
+           "Cmd": ["cat", "/world"],
+           "PortSpecs":["22"]
+       }
 
 
-  **Example response**:
+    **Example response**:
 
 
-  .. sourcecode:: http
+    .. sourcecode:: http
 
 
-    HTTP/1.1 201 OK
-    Content-Type: application/vnd.docker.raw-stream
+       HTTP/1.1 201 OK
+	   Content-Type: application/vnd.docker.raw-stream
 
 
-    {"Id":"596069db4bf5"}
+       {"Id":"596069db4bf5"}
 
 
-  :query container: source container
-  :query repo: repository
-  :query tag: tag
-  :query m: commit message
-  :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
-  :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
-  :statuscode 201: no error
-  :statuscode 404: no such container
-  :statuscode 500: server error
+    :query container: source container
+    :query repo: repository
+    :query tag: tag
+    :query m: commit message
+    :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
+    :statuscode 201: no error
+    :statuscode 404: no such container
+    :statuscode 500: server error
 
 
 Monitor Docker's events
 Monitor Docker's events
 ***********************
 ***********************

+ 16 - 6
docs/sources/api/docker_remote_api_v1.6.rst

@@ -13,9 +13,12 @@ Docker Remote API v1.6
 1. Brief introduction
 1. Brief introduction
 =====================
 =====================
 
 
-- The Remote API is replacing rcli
-- Default port in the docker daemon is 4243
-- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
+- The Remote API has replaced rcli
+- The daemon listens on ``unix:///var/run/docker.sock``, but you can
+  :ref:`bind_docker`.
+- The API tends to be REST, but for some complex commands, like
+  ``attach`` or ``pull``, the HTTP connection is hijacked to transport
+  ``stdout, stdin`` and ``stderr``
 
 
 2. Endpoints
 2. Endpoints
 ============
 ============
@@ -148,6 +151,7 @@ Create a container
 	   }
 	   }
 	
 	
 	:jsonparam config: the container's configuration
 	:jsonparam config: the container's configuration
+ 	:query name: container name to use
 	:statuscode 201: no error
 	:statuscode 201: no error
 	:statuscode 404: no such container
 	:statuscode 404: no such container
 	:statuscode 406: impossible to attach (container not running)
 	:statuscode 406: impossible to attach (container not running)
@@ -442,7 +446,8 @@ Kill a container
 	.. sourcecode:: http
 	.. sourcecode:: http
 
 
 	   HTTP/1.1 204 OK
 	   HTTP/1.1 204 OK
-	   	
+
+	:query signal: Signal to send to the container (integer). When not set, SIGKILL is assumed and the call will waits for the container to exit.
 	:statuscode 204: no error
 	:statuscode 204: no error
 	:statuscode 404: no such container
 	:statuscode 404: no such container
 	:statuscode 500: server error
 	:statuscode 500: server error
@@ -1131,7 +1136,13 @@ Create a new image from a container's changes
 
 
     .. sourcecode:: http
     .. sourcecode:: http
 
 
-        POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
+       Content-Type: application/json
+       
+       {
+           "Cmd": ["cat", "/world"],
+           "PortSpecs":["22"]
+       }
 
 
     **Example response**:
     **Example response**:
 
 
@@ -1147,7 +1158,6 @@ Create a new image from a container's changes
     :query tag: tag
     :query tag: tag
     :query m: commit message
     :query m: commit message
     :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
     :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
-    :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
     :statuscode 201: no error
     :statuscode 201: no error
     :statuscode 404: no such container
     :statuscode 404: no such container
     :statuscode 500: server error
     :statuscode 500: server error

+ 1 - 1
docs/sources/api/index.rst

@@ -14,5 +14,5 @@ Your programs and scripts can access Docker's functionality via these interfaces
   registry_api
   registry_api
   index_api
   index_api
   docker_remote_api
   docker_remote_api
-
+  remote_api_client_libraries
 
 

+ 702 - 38
docs/sources/commandline/cli.rst

@@ -4,11 +4,8 @@
 
 
 .. _cli:
 .. _cli:
 
 
-Overview
-======================
-
-Docker Usage
-~~~~~~~~~~~~~~~~~~
+Command Line Help
+-----------------
 
 
 To list available commands, either run ``docker`` with no parameters or execute
 To list available commands, either run ``docker`` with no parameters or execute
 ``docker help``::
 ``docker help``::
@@ -21,71 +18,738 @@ To list available commands, either run ``docker`` with no parameters or execute
 
 
     ...
     ...
 
 
+.. _cli_attach:
+
+``attach``
+----------
+
+::
+
+    Usage: docker attach CONTAINER
+
+    Attach to a running container.
+
+      -nostdin=false: Do not attach stdin
+      -sig-proxy=true: Proxify all received signal to the process (even in non-tty mode)
+
+You can detach from the container again (and leave it running) with
+``CTRL-c`` (for a quiet exit) or ``CTRL-\`` to get a stacktrace of
+the Docker client when it quits.
+
+To stop a container, use ``docker stop``
+
+To kill the container, use ``docker kill``
+
+.. _cli_attach_examples:
+ 
+Examples:
+~~~~~~~~~
+
+.. code-block:: bash
+
+     $ ID=$(sudo docker run -d ubuntu /usr/bin/top -b)
+     $ sudo docker attach $ID
+     top - 02:05:52 up  3:05,  0 users,  load average: 0.01, 0.02, 0.05
+     Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
+     Cpu(s):  0.1%us,  0.2%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
+     Mem:    373572k total,   355560k used,    18012k free,    27872k buffers
+     Swap:   786428k total,        0k used,   786428k free,   221740k cached
+
+     PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
+      1 root      20   0 17200 1116  912 R    0  0.3   0:00.03 top                
+
+      top - 02:05:55 up  3:05,  0 users,  load average: 0.01, 0.02, 0.05
+      Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
+      Cpu(s):  0.0%us,  0.2%sy,  0.0%ni, 99.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
+      Mem:    373572k total,   355244k used,    18328k free,    27872k buffers
+      Swap:   786428k total,        0k used,   786428k free,   221776k cached
+
+        PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
+	    1 root      20   0 17208 1144  932 R    0  0.3   0:00.03 top                
+
+
+      top - 02:05:58 up  3:06,  0 users,  load average: 0.01, 0.02, 0.05
+      Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
+      Cpu(s):  0.2%us,  0.3%sy,  0.0%ni, 99.5%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
+      Mem:    373572k total,   355780k used,    17792k free,    27880k buffers
+      Swap:   786428k total,        0k used,   786428k free,   221776k cached
+
+      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
+           1 root      20   0 17208 1144  932 R    0  0.3   0:00.03 top                
+     ^C$ 
+     $ sudo docker stop $ID
+
+.. _cli_build:
+
+``build``
+---------
+
+::
+
+    Usage: docker build [OPTIONS] PATH | URL | -
+    Build a new container image from the source code at PATH
+      -t="": Repository name (and optionally a tag) to be applied to the resulting image in case of success.
+      -q=false: Suppress verbose build output.
+      -no-cache: Do not use the cache when building the image.
+      -rm: Remove intermediate containers after a successful build
+    When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context
+
+.. _cli_build_examples:
+
+Examples:
+~~~~~~~~~
+
+.. code-block:: bash
+
+    sudo docker build .
+
+This will read the ``Dockerfile`` from the current directory. It will
+also send any other files and directories found in the current
+directory to the ``docker`` daemon.
+
+The contents of this directory would be used by ``ADD`` commands found
+within the ``Dockerfile``.  This will send a lot of data to the
+``docker`` daemon if the current directory contains a lot of data.  If
+the absolute path is provided instead of ``.`` then only the files and
+directories required by the ADD commands from the ``Dockerfile`` will be
+added to the context and transferred to the ``docker`` daemon.
+
+.. code-block:: bash
+
+   sudo docker build -t vieux/apache:2.0 .
+
+This will build like the previous example, but it will then tag the
+resulting image. The repository name will be ``vieux/apache`` and the
+tag will be ``2.0``
+
+
+.. code-block:: bash
+
+    sudo docker build - < Dockerfile
+
+This will read a ``Dockerfile`` from *stdin* without context. Due to
+the lack of a context, no contents of any local directory will be sent
+to the ``docker`` daemon.  ``ADD`` doesn't work when running in this
+mode because the absence of the context provides no source files to
+copy to the container.
+
+
+.. code-block:: bash
+
+    sudo docker build github.com/creack/docker-firefox
+
+This will clone the Github repository and use it as context. The
+``Dockerfile`` at the root of the repository is used as
+``Dockerfile``.  Note that you can specify an arbitrary git repository
+by using the ``git://`` schema.
+
+
+.. _cli_commit:
+
+``commit``
+----------
+
+::
+
+    Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY [TAG]]
+
+    Create a new image from a container's changes
+
+      -m="": Commit message
+      -author="": Author (eg. "John Hannibal Smith <hannibal@a-team.com>"
+      -run="": Configuration to be applied when the image is launched with `docker run`. 
+               (ex: '{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
+
+Full -run example (multiline is ok within a single quote ``'``)
+
+::
+
+  $ sudo docker commit -run='
+  {
+      "Entrypoint" : null,
+      "Privileged" : false,
+      "User" : "",
+      "VolumesFrom" : "",
+      "Cmd" : ["cat", "-e", "/etc/resolv.conf"],
+      "Dns" : ["8.8.8.8", "8.8.4.4"],
+      "MemorySwap" : 0,
+      "AttachStdin" : false,
+      "AttachStderr" : false,
+      "CpuShares" : 0,
+      "OpenStdin" : false,
+      "Volumes" : null,
+      "Hostname" : "122612f45831",
+      "PortSpecs" : ["22", "80", "443"],
+      "Image" : "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
+      "Tty" : false,
+      "Env" : [
+         "HOME=/",
+         "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+      ],
+      "StdinOnce" : false,
+      "Domainname" : "",
+      "WorkingDir" : "/",
+      "NetworkDisabled" : false,
+      "Memory" : 0,
+      "AttachStdout" : false
+  }' $CONTAINER_ID
+
+.. _cli_cp:
+
+``cp``
+------
+
+::
+
+    Usage: docker cp CONTAINER:RESOURCE HOSTPATH
+
+    Copy files/folders from the containers filesystem to the host
+    path.  Paths are relative to the root of the filesystem.
+
+.. _cli_diff:
+
+``diff``
+--------
+
+::
+
+    Usage: docker diff CONTAINER [OPTIONS]
+
+    Inspect changes on a container's filesystem
+
+.. _cli_events:
+
+``events``
+----------
+
+::
+
+    Usage: docker events
+
+    Get real time events from the server
+
+.. _cli_events_example:
+
+Examples
+~~~~~~~~
+
+You'll need two shells for this example.
+
+Shell 1: Listening for events
+.............................
+
+.. code-block:: bash
+    
+    $ sudo docker events
+
+Shell 2: Start and Stop a Container
+...................................
+
+.. code-block:: bash
+
+    $ sudo docker start 4386fb97867d
+    $ sudo docker stop 4386fb97867d
+
+Shell 1: (Again .. now showing events)
+......................................
+
+.. code-block:: bash
+
+    [2013-09-03 15:49:26 +0200 CEST] 4386fb97867d: (from 12de384bfb10) start
+    [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) die
+    [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) stop
+
+
+.. _cli_export:
+
+``export``
+----------
+
+::
+
+    Usage: docker export CONTAINER
+
+    Export the contents of a filesystem as a tar archive
+
+.. _cli_history:
+
+``history``
+-----------
+
+::
+
+    Usage: docker history [OPTIONS] IMAGE
+
+    Show the history of an image
+
+.. _cli_images:
+
+``images``
+----------
+
+::
+
+    Usage: docker images [OPTIONS] [NAME]
+
+    List images
+
+      -a=false: show all images
+      -q=false: only show numeric IDs
+      -viz=false: output in graphviz format
+
+Displaying images visually
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+    sudo docker images -viz | dot -Tpng -o docker.png
+
+.. image:: docker_images.gif
+   :alt: Example inheritance graph of Docker images.
+
+.. _cli_import:
+
+``import``
+----------
+
+::
+
+    Usage: docker import URL|- [REPOSITORY [TAG]]
+
+    Create a new filesystem image from the contents of a tarball
+
+At this time, the URL must start with ``http`` and point to a single
+file archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) containing a
+root filesystem. If you would like to import from a local directory or
+archive, you can use the ``-`` parameter to take the data from
+standard in.
+
+Examples
+~~~~~~~~
+
+Import from a remote location
+.............................
+
+``$ sudo docker import http://example.com/exampleimage.tgz exampleimagerepo``
+
+Import from a local file
+........................
+
+Import to docker via pipe and standard in
+
+``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal``
+
+Import from a local directory
+.............................
+
+``$ sudo tar -c . | docker import - exampleimagedir``
+
+Note the ``sudo`` in this example -- you must preserve the ownership
+of the files (especially root ownership) during the archiving with
+tar. If you are not root (or sudo) when you tar, then the ownerships
+might not get preserved.
+
+.. _cli_info:
+
+``info``
+--------
+
+::
+
+    Usage: docker info
+
+    Display system-wide information.
+
+.. _cli_insert:
+
+``insert``
+----------
+
+::
+
+    Usage: docker insert IMAGE URL PATH
+
+    Insert a file from URL in the IMAGE at PATH
+
+Examples
+~~~~~~~~
+
+Insert file from github
+.......................
+
+.. code-block:: bash
+
+    $ sudo docker insert 8283e18b24bc https://raw.github.com/metalivedev/django/master/postinstall /tmp/postinstall.sh
+
+.. _cli_inspect:
+
+``inspect``
+-----------
+
+::
+
+    Usage: docker inspect [OPTIONS] CONTAINER
+
+    Return low-level information on a container
+
+.. _cli_kill:
+
+``kill``
+--------
+
+::
+
+    Usage: docker kill CONTAINER [CONTAINER...]
+
+    Kill a running container
+
+.. _cli_login:
+
+``login``
+---------
+
+::
+
+    Usage: docker login [OPTIONS] [SERVER]
+
+    Register or Login to the docker registry server
+
+    -e="": email
+    -p="": password
+    -u="": username
+
+    If you want to login to a private registry you can
+    specify this by adding the server name.
+
+    example:
+    docker login localhost:8080
+
+
+.. _cli_logs:
+
+``logs``
+--------
+
+::
+
+    Usage: docker logs [OPTIONS] CONTAINER
+
+    Fetch the logs of a container
+
+
+.. _cli_port:
+
+``port``
+--------
+
+::
+
+    Usage: docker port [OPTIONS] CONTAINER PRIVATE_PORT
+
+    Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
+
+
+.. _cli_ps:
+
+``ps``
+------
+
+::
+
+    Usage: docker ps [OPTIONS]
+
+    List containers
+
+      -a=false: Show all containers. Only running containers are shown by default.
+      -notrunc=false: Don't truncate output
+      -q=false: Only display numeric IDs
+
+.. _cli_pull:
+
+``pull``
+--------
+
+::
+
+    Usage: docker pull NAME
+
+    Pull an image or a repository from the registry
+
+
+.. _cli_push:
+
+``push``
+--------
+
+::
+
+    Usage: docker push NAME
+
+    Push an image or a repository to the registry
+
+
+.. _cli_restart:
+
+``restart``
+-----------
+
+::
+
+    Usage: docker restart [OPTIONS] NAME
+
+    Restart a running container
+
+.. _cli_rm:
+
+``rm``
+------
+
+::
+
+    Usage: docker rm [OPTIONS] CONTAINER
+
+    Remove one or more containers
+        -link="": Remove the link instead of the actual container
+ 
+
+Examples:
+~~~~~~~~~
+
+.. code-block:: bash
+
+    $ docker rm /redis
+    /redis
+
+
+This will remove the container referenced under the link ``/redis``.
+
+
+.. code-block:: bash
+
+    $ docker rm -link /webapp/redis
+    /webapp/redis
+
+
+This will remove the underlying link between ``/webapp`` and the ``/redis`` containers removing all
+network communication.
+
+.. _cli_rmi:
+
+``rmi``
+-------
+
+::
+
+    Usage: docker rmi IMAGE [IMAGE...]
+
+    Remove one or more images
+
+.. _cli_run:
+
+``run``
+-------
+
+::
+
+    Usage: docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...]
+
+    Run a command in a new container
+
+      -a=map[]: Attach to stdin, stdout or stderr
+      -c=0: CPU shares (relative weight)
+      -cidfile="": Write the container ID to the file
+      -d=false: Detached mode: Run container in the background, print new container id
+      -e=[]: Set environment variables
+      -h="": Container host name
+      -i=false: Keep stdin open even if not attached
+      -privileged=false: Give extended privileges to this container
+      -m=0: Memory limit (in bytes)
+      -n=true: Enable networking for this container
+      -p=[]: Map a network port to the container
+      -rm=false: Automatically remove the container when it exits (incompatible with -d)
+      -t=false: Allocate a pseudo-tty
+      -u="": Username or UID
+      -dns=[]: Set custom dns servers for the container
+      -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume.
+      -volumes-from="": Mount all volumes from the given container
+      -entrypoint="": Overwrite the default entrypoint set by the image
+      -w="": Working directory inside the container
+      -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
+      -sig-proxy=true: Proxify all received signal to the process (even in non-tty mode)
+      -expose=[]: Expose a port from the container without publishing it to your host
+      -link="": Add link to another container (name:alias)
+      -name="": Assign the specified name to the container. If no name is specific docker will generate a random name
+
+Examples
+--------
+
+.. code-block:: bash
+
+    sudo docker run -cidfile /tmp/docker_test.cid ubuntu echo "test"
+
+This will create a container and print "test" to the console. The
+``cidfile`` flag makes docker attempt to create a new file and write the
+container ID to it. If the file exists already, docker will return an
+error. Docker will close this file when docker run exits.
+
+.. code-block:: bash
+
+   docker run mount -t tmpfs none /var/spool/squid
+
+This will *not* work, because by default, most potentially dangerous
+kernel capabilities are dropped; including ``cap_sys_admin`` (which is
+required to mount filesystems). However, the ``-privileged`` flag will
+allow it to run:
+
+.. code-block:: bash
+
+   docker run -privileged mount -t tmpfs none /var/spool/squid
+
+The ``-privileged`` flag gives *all* capabilities to the container,
+and it also lifts all the limitations enforced by the ``device``
+cgroup controller. In other words, the container can then do almost
+everything that the host can do. This flag exists to allow special
+use-cases, like running Docker within Docker.
+
+.. code-block:: bash
+
+   docker  run -w /path/to/dir/ -i -t  ubuntu pwd
+
+The ``-w`` lets the command being executed inside directory given, 
+here /path/to/dir/. If the path does not exists it is created inside the 
+container.
+
+.. code-block:: bash
+
+   docker  run  -v `pwd`:`pwd` -w `pwd` -i -t  ubuntu pwd
+
+The ``-v`` flag mounts the current working directory into the container. 
+The ``-w`` lets the command being executed inside the current 
+working directory, by changing into the directory to the value
+returned by ``pwd``. So this combination executes the command
+using the container, but inside the current working directory.
+
+.. code-block:: bash
+
+    docker run -p 127.0.0.0::80 ubuntu bash
+
+This the ``-p`` flag now allows you to bind a port to a specific
+interface of the host machine.  In this example port ``80`` of the 
+container will have a dynamically allocated port bound to 127.0.0.1 
+of the host.
+
+.. code-block:: bash
+
+    docker run -p 127.0.0.1:80:80 ubuntu bash
+
+This will bind port ``80`` of the container to port ``80`` on 127.0.0.1 of your
+host machine.
+
+.. code-block:: bash
+
+    docker run -expose 80 ubuntu bash
+
+This will expose port ``80`` of the container for use within a link
+without publishing the port to the host system's interfaces.  
+
+.. code-block:: bash
+
+    docker run -name console -t -i ubuntu bash
+
+This will create and run a new container with the container name 
+being ``console``.
+
+.. code-block:: bash
+
+    docker run -link /redis:redis -name console ubuntu bash
+
+The ``-link`` flag will link the container named ``/redis`` into the 
+newly created container with the alias ``redis``.  The new container
+can access the network and environment of the redis container via
+environment variables.  The ``-name`` flag will assign the name ``console`` 
+to the newly created container.
+
+.. _cli_search:
+
+``search``
+----------
+
+::
+
+    Usage: docker search TERM
+
+    Searches for the TERM parameter on the Docker index and prints out
+    a list of repositories that match.
 
 
+.. _cli_start:
 
 
-Available Commands
-~~~~~~~~~~~~~~~~~~
+``start``
+---------
 
 
-.. include:: command/attach.rst
+::
 
 
-.. include:: command/build.rst
+    Usage: docker start [OPTIONS] NAME
 
 
-.. include:: command/commit.rst
+    Start a stopped container
 
 
-.. include:: command/cp.rst
+      -a=false: Attach container's stdout/stderr and forward all signals to the process
+      -i=false: Attach container's stdin
 
 
-.. include:: command/diff.rst
+.. _cli_stop:
 
 
-.. include:: command/events.rst
+``stop``
+--------
 
 
-.. include:: command/export.rst
+::
 
 
-.. include:: command/history.rst
+    Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
 
 
-.. include:: command/images.rst
+    Stop a running container
 
 
-.. include:: command/import.rst
+      -t=10: Number of seconds to wait for the container to stop before killing it.
 
 
-.. include:: command/info.rst
+.. _cli_tag:
 
 
-.. include:: command/insert.rst
+``tag``
+-------
 
 
-.. include:: command/inspect.rst
+::
 
 
-.. include:: command/kill.rst
+    Usage: docker tag [OPTIONS] IMAGE REPOSITORY [TAG]
 
 
-.. include:: command/login.rst
+    Tag an image into a repository
 
 
-.. include:: command/logs.rst
+      -f=false: Force
 
 
-.. include:: command/port.rst
+.. _cli_top:
 
 
-.. include:: command/ps.rst
+``top``
+-------
 
 
-.. include:: command/pull.rst
+::
 
 
-.. include:: command/push.rst
+    Usage: docker top CONTAINER
 
 
-.. include:: command/restart.rst
+    Lookup the running processes of a container
 
 
-.. include:: command/rm.rst
+.. _cli_version:
 
 
-.. include:: command/rmi.rst
+``version``
+-----------
 
 
-.. include:: command/run.rst
+Show the version of the docker client, daemon, and latest released version.
 
 
-.. include:: command/search.rst
 
 
-.. include:: command/start.rst
+.. _cli_wait:
 
 
-.. include:: command/stop.rst
+``wait``
+--------
 
 
-.. include:: command/tag.rst
+::
 
 
-.. include:: command/top.rst
+    Usage: docker wait [OPTIONS] NAME
 
 
-.. include:: command/version.rst
+    Block until a container stops, then print its exit code.
 
 
-.. include:: command/wait.rst
 
 
 
 

+ 0 - 59
docs/sources/commandline/command/attach.rst

@@ -1,59 +0,0 @@
-:title: Attach Command
-:description: Attach to a running container
-:keywords: attach, container, docker, documentation
-
-===========================================
-``attach`` -- Attach to a running container
-===========================================
-
-::
-
-    Usage: docker attach CONTAINER
-
-    Attach to a running container.
-
-You can detach from the container again (and leave it running) with
-``CTRL-c`` (for a quiet exit) or ``CTRL-\`` to get a stacktrace of
-the Docker client when it quits.
-
-To stop a container, use ``docker stop``
-
-To kill the container, use ``docker kill``
- 
-Examples:
----------
-
-.. code-block:: bash
-
-     $ ID=$(sudo docker run -d ubuntu /usr/bin/top -b)
-     $ sudo docker attach $ID
-     top - 02:05:52 up  3:05,  0 users,  load average: 0.01, 0.02, 0.05
-     Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
-     Cpu(s):  0.1%us,  0.2%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
-     Mem:    373572k total,   355560k used,    18012k free,    27872k buffers
-     Swap:   786428k total,        0k used,   786428k free,   221740k cached
-
-     PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
-      1 root      20   0 17200 1116  912 R    0  0.3   0:00.03 top                
-
-      top - 02:05:55 up  3:05,  0 users,  load average: 0.01, 0.02, 0.05
-      Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
-      Cpu(s):  0.0%us,  0.2%sy,  0.0%ni, 99.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
-      Mem:    373572k total,   355244k used,    18328k free,    27872k buffers
-      Swap:   786428k total,        0k used,   786428k free,   221776k cached
-
-        PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
-	    1 root      20   0 17208 1144  932 R    0  0.3   0:00.03 top                
-
-
-      top - 02:05:58 up  3:06,  0 users,  load average: 0.01, 0.02, 0.05
-      Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
-      Cpu(s):  0.2%us,  0.3%sy,  0.0%ni, 99.5%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
-      Mem:    373572k total,   355780k used,    17792k free,    27880k buffers
-      Swap:   786428k total,        0k used,   786428k free,   221776k cached
-
-      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
-           1 root      20   0 17208 1144  932 R    0  0.3   0:00.03 top                
-     ^C$ 
-     $ sudo docker stop $ID
-

+ 0 - 65
docs/sources/commandline/command/build.rst

@@ -1,65 +0,0 @@
-:title: Build Command
-:description: Build a new image from the Dockerfile passed via stdin
-:keywords: build, docker, container, documentation
-
-================================================
-``build`` -- Build a container from a Dockerfile
-================================================
-
-::
-
-    Usage: docker build [OPTIONS] PATH | URL | -
-    Build a new container image from the source code at PATH
-      -t="": Repository name (and optionally a tag) to be applied to the resulting image in case of success.
-      -q=false: Suppress verbose build output.
-      -no-cache: Do not use the cache when building the image.
-      -rm: Remove intermediate containers after a successful build
-    When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context
-
-
-Examples
---------
-
-.. code-block:: bash
-
-    sudo docker build .
-
-This will read the ``Dockerfile`` from the current directory. It will
-also send any other files and directories found in the current
-directory to the ``docker`` daemon.
-
-The contents of this directory would be used by ``ADD`` commands found
-within the ``Dockerfile``.  This will send a lot of data to the
-``docker`` daemon if the current directory contains a lot of data.  If
-the absolute path is provided instead of ``.`` then only the files and
-directories required by the ADD commands from the ``Dockerfile`` will be
-added to the context and transferred to the ``docker`` daemon.
-
-.. code-block:: bash
-
-   sudo docker build -t vieux/apache:2.0 .
-
-This will build like the previous example, but it will then tag the
-resulting image. The repository name will be ``vieux/apache`` and the
-tag will be ``2.0``
-
-
-.. code-block:: bash
-
-    sudo docker build - < Dockerfile
-
-This will read a ``Dockerfile`` from *stdin* without context. Due to
-the lack of a context, no contents of any local directory will be sent
-to the ``docker`` daemon.  ``ADD`` doesn't work when running in this
-mode because the absence of the context provides no source files to
-copy to the container.
-
-
-.. code-block:: bash
-
-    sudo docker build github.com/creack/docker-firefox
-
-This will clone the Github repository and use it as context. The
-``Dockerfile`` at the root of the repository is used as
-``Dockerfile``.  Note that you can specify an arbitrary git repository
-by using the ``git://`` schema.

+ 0 - 52
docs/sources/commandline/command/commit.rst

@@ -1,52 +0,0 @@
-:title: Commit Command
-:description: Create a new image from a container's changes
-:keywords: commit, docker, container, documentation
-
-===========================================================
-``commit`` -- Create a new image from a container's changes
-===========================================================
-
-::
-
-    Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY [TAG]]
-
-    Create a new image from a container's changes
-
-      -m="": Commit message
-      -author="": Author (eg. "John Hannibal Smith <hannibal@a-team.com>"
-      -run="": Configuration to be applied when the image is launched with `docker run`. 
-               (ex: '{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
-
-Full -run example (multiline is ok within a single quote ``'``)
-
-::
-
-  $ sudo docker commit -run='
-  {
-      "Entrypoint" : null,
-      "Privileged" : false,
-      "User" : "",
-      "VolumesFrom" : "",
-      "Cmd" : ["cat", "-e", "/etc/resolv.conf"],
-      "Dns" : ["8.8.8.8", "8.8.4.4"],
-      "MemorySwap" : 0,
-      "AttachStdin" : false,
-      "AttachStderr" : false,
-      "CpuShares" : 0,
-      "OpenStdin" : false,
-      "Volumes" : null,
-      "Hostname" : "122612f45831",
-      "PortSpecs" : ["22", "80", "443"],
-      "Image" : "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
-      "Tty" : false,
-      "Env" : [
-         "HOME=/",
-         "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
-      ],
-      "StdinOnce" : false,
-      "Domainname" : "",
-      "WorkingDir" : "/",
-      "NetworkDisabled" : false,
-      "Memory" : 0,
-      "AttachStdout" : false
-  }' $CONTAINER_ID

+ 0 - 14
docs/sources/commandline/command/cp.rst

@@ -1,14 +0,0 @@
-:title: Cp Command
-:description: Copy files/folders from the containers filesystem to the host path
-:keywords: cp, docker, container, documentation, copy
-
-============================================================================
-``cp`` -- Copy files/folders from the containers filesystem to the host path
-============================================================================
-
-::
-
-    Usage: docker cp CONTAINER:RESOURCE HOSTPATH
-
-    Copy files/folders from the containers filesystem to the host
-    path.  Paths are relative to the root of the filesystem.

+ 0 - 13
docs/sources/commandline/command/diff.rst

@@ -1,13 +0,0 @@
-:title: Diff Command
-:description: Inspect changes on a container's filesystem
-:keywords: diff, docker, container, documentation
-
-=======================================================
-``diff`` -- Inspect changes on a container's filesystem
-=======================================================
-
-::
-
-    Usage: docker diff CONTAINER [OPTIONS]
-
-    Inspect changes on a container's filesystem

+ 0 - 34
docs/sources/commandline/command/events.rst

@@ -1,34 +0,0 @@
-:title: Events Command
-:description: Get real time events from the server
-:keywords: events, docker, documentation
-
-=================================================================
-``events`` -- Get real time events from the server
-=================================================================
-
-::
-
-    Usage: docker events
-
-    Get real time events from the server
-
-Examples
---------
-
-Starting and stopping a container
-.................................
-
-.. code-block:: bash
-
-    $ sudo docker start 4386fb97867d
-    $ sudo docker stop 4386fb97867d
-
-In another shell
-
-.. code-block:: bash
-    
-    $ sudo docker events
-    [2013-09-03 15:49:26 +0200 CEST] 4386fb97867d: (from 12de384bfb10) start
-    [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) die
-    [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) stop
-

+ 0 - 13
docs/sources/commandline/command/export.rst

@@ -1,13 +0,0 @@
-:title: Export Command
-:description: Export the contents of a filesystem as a tar archive
-:keywords: export, docker, container, documentation
-
-=================================================================
-``export`` -- Stream the contents of a container as a tar archive
-=================================================================
-
-::
-
-    Usage: docker export CONTAINER
-
-    Export the contents of a filesystem as a tar archive

+ 0 - 13
docs/sources/commandline/command/history.rst

@@ -1,13 +0,0 @@
-:title: History Command
-:description: Show the history of an image
-:keywords: history, docker, container, documentation
-
-===========================================
-``history`` -- Show the history of an image
-===========================================
-
-::
-
-    Usage: docker history [OPTIONS] IMAGE
-
-    Show the history of an image

+ 0 - 26
docs/sources/commandline/command/images.rst

@@ -1,26 +0,0 @@
-:title: Images Command
-:description: List images
-:keywords: images, docker, container, documentation
-
-=========================
-``images`` -- List images
-=========================
-
-::
-
-    Usage: docker images [OPTIONS] [NAME]
-
-    List images
-
-      -a=false: show all images
-      -q=false: only show numeric IDs
-      -viz=false: output in graphviz format
-
-Displaying images visually
---------------------------
-
-::
-
-    sudo docker images -viz | dot -Tpng -o docker.png
-
-.. image:: https://docs.docker.io/en/latest/_static/docker_images.gif

+ 0 - 44
docs/sources/commandline/command/import.rst

@@ -1,44 +0,0 @@
-:title: Import Command
-:description: Create a new filesystem image from the contents of a tarball
-:keywords: import, tarball, docker, url, documentation
-
-==========================================================================
-``import`` -- Create a new filesystem image from the contents of a tarball
-==========================================================================
-
-::
-
-    Usage: docker import URL|- [REPOSITORY [TAG]]
-
-    Create a new filesystem image from the contents of a tarball
-
-At this time, the URL must start with ``http`` and point to a single
-file archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) containing a
-root filesystem. If you would like to import from a local directory or
-archive, you can use the ``-`` parameter to take the data from
-standard in.
-
-Examples
---------
-
-Import from a remote location
-.............................
-
-``$ sudo docker import http://example.com/exampleimage.tgz exampleimagerepo``
-
-Import from a local file
-........................
-
-Import to docker via pipe and standard in
-
-``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal``
-
-Import from a local directory
-.............................
-
-``$ sudo tar -c . | docker import - exampleimagedir``
-
-Note the ``sudo`` in this example -- you must preserve the ownership
-of the files (especially root ownership) during the archiving with
-tar. If you are not root (or sudo) when you tar, then the ownerships
-might not get preserved.

+ 0 - 13
docs/sources/commandline/command/info.rst

@@ -1,13 +0,0 @@
-:title: Info Command
-:description: Display system-wide information.
-:keywords: info, docker, information, documentation
-
-===========================================
-``info`` -- Display system-wide information
-===========================================
-
-::
-
-    Usage: docker info
-
-    Display system-wide information.

+ 0 - 23
docs/sources/commandline/command/insert.rst

@@ -1,23 +0,0 @@
-:title: Insert Command
-:description: Insert a file in an image
-:keywords: insert, image, docker, documentation
-
-==========================================================================
-``insert`` -- Insert a file in an image
-==========================================================================
-
-::
-
-    Usage: docker insert IMAGE URL PATH
-
-    Insert a file from URL in the IMAGE at PATH
-
-Examples
---------
-
-Insert file from github
-.......................
-
-.. code-block:: bash
-
-    $ sudo docker insert 8283e18b24bc https://raw.github.com/metalivedev/django/master/postinstall /tmp/postinstall.sh

+ 0 - 13
docs/sources/commandline/command/inspect.rst

@@ -1,13 +0,0 @@
-:title: Inspect Command
-:description: Return low-level information on a container
-:keywords: inspect, container, docker, documentation
-
-==========================================================
-``inspect`` -- Return low-level information on a container
-==========================================================
-
-::
-
-    Usage: docker inspect [OPTIONS] CONTAINER
-
-    Return low-level information on a container

+ 0 - 13
docs/sources/commandline/command/kill.rst

@@ -1,13 +0,0 @@
-:title: Kill Command
-:description: Kill a running container
-:keywords: kill, container, docker, documentation
-
-====================================
-``kill`` -- Kill a running container
-====================================
-
-::
-
-    Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
-
-    Kill a running container

+ 0 - 24
docs/sources/commandline/command/login.rst

@@ -1,24 +0,0 @@
-:title: Login Command
-:description: Register or Login to the docker registry server
-:keywords: login, docker, documentation
-
-============================================================
-``login`` -- Register or Login to the docker registry server
-============================================================
-
-::
-
-    Usage: docker login [OPTIONS] [SERVER]
-
-    Register or Login to the docker registry server
-
-    -e="": email
-    -p="": password
-    -u="": username
-
-    If you want to login to a private registry you can
-    specify this by adding the server name.
-
-    example:
-    docker login localhost:8080
-

+ 0 - 13
docs/sources/commandline/command/logs.rst

@@ -1,13 +0,0 @@
-:title: Logs Command
-:description: Fetch the logs of a container
-:keywords: logs, container, docker, documentation
-
-=========================================
-``logs`` -- Fetch the logs of a container
-=========================================
-
-::
-
-    Usage: docker logs [OPTIONS] CONTAINER
-
-    Fetch the logs of a container

+ 0 - 13
docs/sources/commandline/command/port.rst

@@ -1,13 +0,0 @@
-:title: Port Command
-:description: Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
-:keywords: port, docker, container, documentation
-
-=========================================================================
-``port`` -- Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
-=========================================================================
-
-::
-
-    Usage: docker port [OPTIONS] CONTAINER PRIVATE_PORT
-
-    Lookup the public-facing port which is NAT-ed to PRIVATE_PORT

+ 0 - 17
docs/sources/commandline/command/ps.rst

@@ -1,17 +0,0 @@
-:title: Ps Command
-:description: List containers
-:keywords: ps, docker, documentation, container
-
-=========================
-``ps`` -- List containers
-=========================
-
-::
-
-    Usage: docker ps [OPTIONS]
-
-    List containers
-
-      -a=false: Show all containers. Only running containers are shown by default.
-      -notrunc=false: Don't truncate output
-      -q=false: Only display numeric IDs

+ 0 - 13
docs/sources/commandline/command/pull.rst

@@ -1,13 +0,0 @@
-:title: Pull Command
-:description: Pull an image or a repository from the registry
-:keywords: pull, image, repo, repository, documentation, docker
-
-=========================================================================
-``pull`` -- Pull an image or a repository from the docker registry server
-=========================================================================
-
-::
-
-    Usage: docker pull NAME
-
-    Pull an image or a repository from the registry

+ 0 - 13
docs/sources/commandline/command/push.rst

@@ -1,13 +0,0 @@
-:title: Push Command
-:description: Push an image or a repository to the registry
-:keywords: push, docker, image, repository, documentation, repo
-
-=======================================================================
-``push`` -- Push an image or a repository to the docker registry server
-=======================================================================
-
-::
-
-    Usage: docker push NAME
-
-    Push an image or a repository to the registry

+ 0 - 13
docs/sources/commandline/command/restart.rst

@@ -1,13 +0,0 @@
-:title: Restart Command
-:description: Restart a running container
-:keywords: restart, container, docker, documentation
-
-==========================================
-``restart`` -- Restart a running container
-==========================================
-
-::
-
-    Usage: docker restart [OPTIONS] NAME
-
-    Restart a running container

+ 0 - 13
docs/sources/commandline/command/rm.rst

@@ -1,13 +0,0 @@
-:title: Rm Command
-:description: Remove a container
-:keywords: remove, container, docker, documentation, rm
-
-============================
-``rm`` -- Remove a container
-============================
-
-::
-
-    Usage: docker rm [OPTIONS] CONTAINER
-
-    Remove one or more containers

+ 0 - 13
docs/sources/commandline/command/rmi.rst

@@ -1,13 +0,0 @@
-:title: Rmi Command
-:description: Remove an image
-:keywords: rmi, remove, image, docker, documentation
-
-==========================
-``rmi`` -- Remove an image
-==========================
-
-::
-
-    Usage: docker rmi IMAGE [IMAGE...]
-
-    Remove one or more images

+ 0 - 85
docs/sources/commandline/command/run.rst

@@ -1,85 +0,0 @@
-:title: Run Command
-:description: Run a command in a new container
-:keywords: run, container, docker, documentation 
-
-===========================================
-``run`` -- Run a command in a new container
-===========================================
-
-::
-
-    Usage: docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...]
-
-    Run a command in a new container
-
-      -a=map[]: Attach to stdin, stdout or stderr.
-      -c=0: CPU shares (relative weight)
-      -cidfile="": Write the container ID to the file
-      -d=false: Detached mode: Run container in the background, print new container id
-      -e=[]: Set environment variables
-      -h="": Container host name
-      -i=false: Keep stdin open even if not attached
-      -privileged=false: Give extended privileges to this container
-      -m=0: Memory limit (in bytes)
-      -n=true: Enable networking for this container
-      -p=[]: Map a network port to the container
-      -rm=false: Automatically remove the container when it exits (incompatible with -d)
-      -t=false: Allocate a pseudo-tty
-      -u="": Username or UID
-      -dns=[]: Set custom dns servers for the container
-      -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume.
-      -volumes-from="": Mount all volumes from the given container.
-      -entrypoint="": Overwrite the default entrypoint set by the image.
-      -w="": Working directory inside the container
-      -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
-
-Examples
---------
-
-.. code-block:: bash
-
-    sudo docker run -cidfile /tmp/docker_test.cid ubuntu echo "test"
-
-This will create a container and print "test" to the console. The
-``cidfile`` flag makes docker attempt to create a new file and write the
-container ID to it. If the file exists already, docker will return an
-error. Docker will close this file when docker run exits.
-
-.. code-block:: bash
-
-   docker run mount -t tmpfs none /var/spool/squid
-
-This will *not* work, because by default, most potentially dangerous
-kernel capabilities are dropped; including ``cap_sys_admin`` (which is
-required to mount filesystems). However, the ``-privileged`` flag will
-allow it to run:
-
-.. code-block:: bash
-
-   docker run -privileged mount -t tmpfs none /var/spool/squid
-
-The ``-privileged`` flag gives *all* capabilities to the container,
-and it also lifts all the limitations enforced by the ``device``
-cgroup controller. In other words, the container can then do almost
-everything that the host can do. This flag exists to allow special
-use-cases, like running Docker within Docker.
-
-.. code-block:: bash
-
-   docker  run -w /path/to/dir/ -i -t  ubuntu pwd
-
-The ``-w`` lets the command being executed inside directory given, 
-here /path/to/dir/. If the path does not exists it is created inside the 
-container.
-
-.. code-block:: bash
-
-   docker  run  -v `pwd`:`pwd` -w `pwd` -i -t  ubuntu pwd
-
-The ``-v`` flag mounts the current working directory into the container. 
-The ``-w`` lets the command being executed inside the current 
-working directory, by changing into the directory to the value
-returned by ``pwd``. So this combination executes the command
-using the container, but inside the current working directory.
-
-

+ 0 - 14
docs/sources/commandline/command/search.rst

@@ -1,14 +0,0 @@
-:title: Search Command
-:description: Searches for the TERM parameter on the Docker index and prints out a list of repositories that match.
-:keywords: search, docker, image, documentation 
-
-===================================================================
-``search`` -- Search for an image in the docker index
-===================================================================
-
-::
-
-    Usage: docker search TERM
-
-    Searches for the TERM parameter on the Docker index and prints out
-    a list of repositories that match.

+ 0 - 13
docs/sources/commandline/command/start.rst

@@ -1,13 +0,0 @@
-:title: Start Command
-:description: Start a stopped container
-:keywords: start, docker, container, documentation
-
-======================================
-``start`` -- Start a stopped container
-======================================
-
-::
-
-    Usage: docker start [OPTIONS] NAME
-
-    Start a stopped container

+ 0 - 15
docs/sources/commandline/command/stop.rst

@@ -1,15 +0,0 @@
-:title: Stop Command
-:description: Stop a running container
-:keywords: stop, container, docker, documentation
-
-====================================
-``stop`` -- Stop a running container
-====================================
-
-::
-
-    Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
-
-    Stop a running container
-
-      -t=10: Number of seconds to wait for the container to stop before killing it.

+ 0 - 15
docs/sources/commandline/command/tag.rst

@@ -1,15 +0,0 @@
-:title: Tag Command
-:description: Tag an image into a repository
-:keywords: tag, docker, image, repository, documentation, repo
-
-=========================================
-``tag`` -- Tag an image into a repository
-=========================================
-
-::
-
-    Usage: docker tag [OPTIONS] IMAGE REPOSITORY [TAG]
-
-    Tag an image into a repository
-
-      -f=false: Force

+ 0 - 13
docs/sources/commandline/command/top.rst

@@ -1,13 +0,0 @@
-:title: Top Command
-:description: Lookup the running processes of a container
-:keywords: top, docker, container, documentation
-
-=======================================================
-``top`` -- Lookup the running processes of a container
-=======================================================
-
-::
-
-    Usage: docker top CONTAINER
-
-    Lookup the running processes of a container

+ 0 - 7
docs/sources/commandline/command/version.rst

@@ -1,7 +0,0 @@
-:title: Version Command
-:description: 
-:keywords: version, docker, documentation
-
-==================================================
-``version`` -- Show the docker version information
-==================================================

+ 0 - 13
docs/sources/commandline/command/wait.rst

@@ -1,13 +0,0 @@
-:title: Wait Command
-:description: Block until a container stops, then print its exit code.
-:keywords: wait, docker, container, documentation
-
-===================================================================
-``wait`` -- Block until a container stops, then print its exit code
-===================================================================
-
-::
-
-    Usage: docker wait [OPTIONS] NAME
-
-    Block until a container stops, then print its exit code.

+ 0 - 0
docs/sources/static_files/docker_images.gif → docs/sources/commandline/docker_images.gif


+ 2 - 33
docs/sources/commandline/index.rst

@@ -1,6 +1,6 @@
 :title: Commands
 :title: Commands
-:description: -- todo: change me
-:keywords: todo, commands, command line, help, docker, documentation
+:description: docker command line interface
+:keywords: commands, command line, help, docker
 
 
 
 
 Commands
 Commands
@@ -12,34 +12,3 @@ Contents:
   :maxdepth: 1
   :maxdepth: 1
 
 
   cli
   cli
-  attach  <command/attach>
-  build   <command/build>
-  commit  <command/commit>
-  cp      <command/cp>
-  diff    <command/diff>
-  events  <command/events>
-  export  <command/export>
-  history <command/history>
-  images  <command/images>
-  import  <command/import>
-  info    <command/info>
-  insert  <command/insert>
-  inspect <command/inspect>
-  kill    <command/kill>
-  login   <command/login>
-  logs    <command/logs>
-  port    <command/port>
-  ps      <command/ps>
-  pull    <command/pull>
-  push    <command/push>
-  restart <command/restart>
-  rm      <command/rm>
-  rmi     <command/rmi>
-  run     <command/run>
-  search  <command/search>
-  start   <command/start>
-  stop    <command/stop>
-  tag     <command/tag>
-  top     <command/top>
-  version <command/version>
-  wait    <command/wait>

+ 14 - 1
docs/sources/contributing/contributing.rst

@@ -5,5 +5,18 @@
 Contributing to Docker
 Contributing to Docker
 ======================
 ======================
 
 
-Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started <https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`_.
+Want to hack on Docker? Awesome! 
 
 
+The repository includes `all the instructions you need to get
+started <https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`_.
+
+The developer environment `Dockerfile <https://github.com/dotcloud/docker/blob/master/Dockerfile>`_
+specifies the tools and versions used to test and build Docker.
+
+If you're making changes to the documentation, see the
+`README.md <https://github.com/dotcloud/docker/blob/master/docs/README.md>`_.
+
+The documentation environment `Dockerfile <https://github.com/dotcloud/docker/blob/master/docs/Dockerfile>`_
+specifies the tools and versions used to build the Documentation.
+
+Further interesting details can be found in the `Packaging hints <https://github.com/dotcloud/docker/blob/master/hack/PACKAGERS.md>`_.

+ 1 - 1
docs/sources/examples/hello_world.rst

@@ -178,4 +178,4 @@ you could skip to any of the other examples:
 * :ref:`running_ssh_service`
 * :ref:`running_ssh_service`
 * :ref:`running_couchdb_service`
 * :ref:`running_couchdb_service`
 * :ref:`postgresql_service`
 * :ref:`postgresql_service`
-* :ref:`mongodb`
+* :ref:`mongodb_image`

+ 2 - 1
docs/sources/examples/index.rst

@@ -1,6 +1,6 @@
 :title: Docker Examples
 :title: Docker Examples
 :description: Examples on how to use Docker
 :description: Examples on how to use Docker
-:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql
+:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql, link
 
 
 
 
 .. _example_list:
 .. _example_list:
@@ -24,3 +24,4 @@ to more substantial services like you might find in production.
    postgresql_service
    postgresql_service
    mongodb
    mongodb
    running_riak_service
    running_riak_service
+   linking_into_redis

+ 126 - 0
docs/sources/examples/linking_into_redis.rst

@@ -0,0 +1,126 @@
+:title: Linking to an Redis container
+:description: Running redis linked into your web app
+:keywords: docker, example, networking, redis, link
+
+.. _linking_redis:
+
+Linking Redis
+=============
+
+.. include:: example_header.inc
+
+Building a redis container to link as a child of our web application.
+
+Building the redis container
+----------------------------
+
+We will use a pre-build version of redis from the index under 
+the name ``crosbymichael/redis``.  If you are interested in the 
+Dockerfile that was used to build this container here it is.
+
+.. code-block:: bash
+    
+    # Build redis from source
+    # Make sure you have the redis source code checked out in
+    # the same directory as this Dockerfile
+    FROM ubuntu
+
+    RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
+    RUN apt-get update
+    RUN apt-get upgrade -y
+
+    RUN apt-get install -y gcc make g++ build-essential libc6-dev tcl
+
+    ADD . /redis
+
+    RUN (cd /redis && make)
+    RUN (cd /redis && make test)
+
+    RUN mkdir -p /redis-data
+    VOLUME ["/redis-data"]
+    EXPOSE 6379
+
+    ENTRYPOINT ["/redis/src/redis-server"]
+    CMD ["--dir", "/redis-data"]
+
+
+We need to ``EXPOSE`` the default port of 6379 so that our link knows what ports 
+to connect to our redis container on.  If you do not expose any ports for the
+image then docker will not be able to establish the link between containers.
+
+
+Run the redis container
+-----------------------
+
+.. code-block:: bash
+    
+    docker run -d -e PASSWORD=docker -name redis crosbymichael/redis --requirepass=docker
+ 
+This will run our redis container using the default port of 6379 and using docker 
+as password to secure our service.  By specifying the ``-name`` flag on run 
+we will assign the name ``redis`` to this container. 
+We can issue all the commands that you would expect; start, stop, attach, using the name.
+The name also allows us to link other containers into this one.  If you do not specify a 
+name on docker run, docker will automatically generate a name for your container. 
+
+Linking redis as a child
+------------------------
+
+Next we can start a new web application that has a dependency on redis and apply a link 
+to connect both containers.  If you noticed when running our redis service we did not use
+the ``-p`` option to publish the redis port to the host system.  Redis exposed port 6379
+but we did not publish the port.  This allows docker to prevent all network traffic to
+the redis container except when explicitly specified within a link.  This is a big win
+for security.  
+
+
+Now lets start our web application with a link into redis.
+
+.. code-block:: bash
+   
+    docker run -t -i -link /redis:db -name webapp ubuntu bash
+
+    root@4c01db0b339c:/# env
+
+    HOSTNAME=4c01db0b339c
+    DB_NAME=/webapp/db
+    TERM=xterm
+    DB_PORT=tcp://172.17.0.8:6379
+    DB_PORT_6379_TCP=tcp://172.17.0.8:6379
+    DB_PORT_6379_TCP_PROTO=tcp
+    DB_PORT_6379_TCP_ADDR=172.17.0.8
+    DB_PORT_6379_TCP_PORT=6379
+    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+    PWD=/
+    DB_ENV_PASSWORD=dockerpass
+    SHLVL=1
+    HOME=/
+    container=lxc
+    _=/usr/bin/env
+    root@4c01db0b339c:/#
+
+
+When we inspect the environment of the linked container we can see a few extra environment 
+variables have been added.  When you specified ``-link /redis:db`` you are telling docker
+to link the container named ``/redis`` into this new container with the alias ``db``.  
+Environment variables are prefixed with the alias so that the parent container can access
+network and environment information from the child.
+
+.. code-block:: bash
+
+    # The name of the child container
+    DB_NAME=/webapp/db
+    # The default protocol, ip, and port of the service running in the container
+    DB_PORT=tcp://172.17.0.8:6379
+    # A specific protocol, ip, and port of various services
+    DB_PORT_6379_TCP=tcp://172.17.0.8:6379
+    DB_PORT_6379_TCP_PROTO=tcp
+    DB_PORT_6379_TCP_ADDR=172.17.0.8
+    DB_PORT_6379_TCP_PORT=6379
+    # Get environment variables of the container 
+    DB_ENV_PASSWORD=dockerpass
+
+
+Accessing the network information along with the environment of the child container allows
+us to easily connect to the redis service on the specific ip and port and use the password
+specified in the environment.

+ 0 - 2
docs/sources/index.rst

@@ -2,8 +2,6 @@
 :description: An overview of the Docker Documentation
 :description: An overview of the Docker Documentation
 :keywords: containers, lxc, concepts, explanation
 :keywords: containers, lxc, concepts, explanation
 
 
-.. image:: https://www.docker.io/static/img/linked/dockerlogo-horizontal.png
-
 Introduction
 Introduction
 ------------
 ------------
 
 

+ 1 - 1
docs/sources/installation/kernel.rst

@@ -69,7 +69,7 @@ to run LXC containers. Note that 2.6.32 has some documented issues regarding
 network namespace setup and teardown; those issues are not a risk if you
 network namespace setup and teardown; those issues are not a risk if you
 run containers in a private environment, but can lead to denial-of-service
 run containers in a private environment, but can lead to denial-of-service
 attacks if you want to run untrusted code in your containers. For more details,
 attacks if you want to run untrusted code in your containers. For more details,
-see `[LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
+see `LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
 
 
 Kernels 2.6.38, and every version since 3.2, have been deployed successfully
 Kernels 2.6.38, and every version since 3.2, have been deployed successfully
 to run containerized production workloads. Feature-wise, there is no huge
 to run containerized production workloads. Feature-wise, there is no huge

+ 4 - 2
docs/sources/installation/ubuntulinux.rst

@@ -78,7 +78,8 @@ Docker is available as a Debian package, which makes installation easy.
    sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"
    sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"
 
 
    # Add the Docker repository to your apt sources list.
    # Add the Docker repository to your apt sources list.
-   sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
+   sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\
+   > /etc/apt/sources.list.d/docker.list"
 
 
    # Update your sources
    # Update your sources
    sudo apt-get update
    sudo apt-get update
@@ -132,7 +133,8 @@ to follow them again.*
    sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"
    sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"
 
 
    # Add the Docker repository to your apt sources list.
    # Add the Docker repository to your apt sources list.
-   sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
+   sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\
+   > /etc/apt/sources.list.d/docker.list"
 
 
    # update
    # update
    sudo apt-get update
    sudo apt-get update

+ 1 - 1
docs/sources/use/baseimages.rst

@@ -40,4 +40,4 @@ Docker Github Repo:
 * `CentOS
 * `CentOS
   <https://github.com/dotcloud/docker/blob/master/contrib/mkimage-centos.sh>`_
   <https://github.com/dotcloud/docker/blob/master/contrib/mkimage-centos.sh>`_
 * `Debian/Ubuntu
 * `Debian/Ubuntu
-  <https://github.com/dotcloud/docker/blob/master/contrib/mkimage-debian.sh>`_
+  <https://github.com/dotcloud/docker/blob/master/contrib/mkimage-debootstrap.sh>`_

+ 3 - 1
docs/sources/use/basics.rst

@@ -67,6 +67,8 @@ you don't need to add ``sudo`` to all the client commands.
   # Restart the docker daemon
   # Restart the docker daemon
   sudo service docker restart
   sudo service docker restart
 
 
+.. _bind_docker:
+
 Bind Docker to another host/port or a Unix socket
 Bind Docker to another host/port or a Unix socket
 -------------------------------------------------
 -------------------------------------------------
 
 
@@ -142,7 +144,7 @@ Expose a service on a TCP port
 .. code-block:: bash
 .. code-block:: bash
 
 
   # Expose port 4444 of this container, and tell netcat to listen on it
   # Expose port 4444 of this container, and tell netcat to listen on it
-  JOB=$(sudo docker run -d -p 4444 ubuntu:12.10 /bin/nc -l -p 4444)
+  JOB=$(sudo docker run -d -p 4444 ubuntu:12.10 /bin/nc -l 4444)
 
 
   # Which public port is NATed to my container?
   # Which public port is NATed to my container?
   PORT=$(sudo docker port $JOB 4444)
   PORT=$(sudo docker port $JOB 4444)

+ 1 - 1
docs/sources/use/builder.rst

@@ -220,7 +220,7 @@ The copy obeys the following rules:
   (``http://example.com`` will not work).
   (``http://example.com`` will not work).
 * If ``<src>`` is a directory, the entire directory is copied,
 * If ``<src>`` is a directory, the entire directory is copied,
   including filesystem metadata.
   including filesystem metadata.
-* If ``<src>``` is a tar archive in a recognized compression format
+* If ``<src>`` is a tar archive in a recognized compression format
   (identity, gzip, bzip2 or xz), it is unpacked as a directory.
   (identity, gzip, bzip2 or xz), it is unpacked as a directory.
 
 
   When a directory is copied or unpacked, it has the same behavior as
   When a directory is copied or unpacked, it has the same behavior as

+ 125 - 0
docs/sources/use/host_integration.rst

@@ -0,0 +1,125 @@
+:title: Host Integration
+:description: How to generate scripts for upstart, systemd, etc.
+:keywords: systemd, upstart, supervisor, docker, documentation, host integration
+
+
+
+Host Integration
+================
+
+You can use your Docker containers with process managers like ``upstart``,
+``systemd`` and ``supervisor``.
+
+Introduction
+------------
+
+When you have finished setting up your image and are happy with your
+running container, you may want to use a process manager to manage
+it. To help with this, we provide a simple image: ``creack/manager:min``
+
+This image takes the container ID as parameter. We also can specify
+the kind of process manager and metadata like *Author* and
+*Description*. The output will will be text suitable for a
+configuration file, echoed to stdout. It is up to you to create the
+.conf file (for `upstart
+<http://upstart.ubuntu.com/cookbook/#job-configuration-file>`_) or
+.service file (for `systemd
+<http://0pointer.de/public/systemd-man/systemd.service.html>`_) and
+put it in the right place for your system.
+
+Usage
+-----
+
+.. code-block:: bash
+
+   docker run creack/manager:min [OPTIONS] <container id>
+
+.. program:: docker run creack/manager:min
+
+.. cmdoption:: -a="<none>" 
+
+   Author of the image
+
+.. cmdoption:: -d="<none>"
+
+   Description of the image
+
+.. cmdoption:: -t="upstart" 
+
+   Type of manager requested: ``upstart`` or ``systemd``
+
+Example Output
+..............
+
+.. code-block:: bash
+
+   docker run creack/manager:min -t="systemd" b28605f2f9a4
+   [Unit]
+   	Description=<none>
+   	Author=<none>
+   	After=docker.service
+
+   [Service]
+   	Restart=always
+   	ExecStart=/usr/bin/docker start -a b28605f2f9a4
+   	ExecStop=/usr/bin/docker stop -t 2 b28605f2f9a4
+
+   [Install]
+   	WantedBy=local.target
+
+
+
+Development
+-----------
+
+The image ``creack/manager:min`` is a ``busybox`` base with the
+compiled binary of ``manager.go`` as the :ref:`Entrypoint
+<entrypoint_def>`.  It is meant to be light and fast to download.
+
+If you would like to change or add things, you can download the full
+``creack/manager`` repository that contains ``creack/manager:min`` and
+``creack/manager:dev``.
+
+The Dockerfiles and the sources are available in
+`/contrib/host_integration
+<https://github.com/dotcloud/docker/tree/master/contrib/host_integration>`_.
+
+
+Upstart
+-------
+
+Upstart is the default process manager. The generated script will
+start the container after the ``docker`` daemon. If the container
+dies, it will respawn.  Start/Restart/Stop/Reload are
+supported. Reload will send a SIGHUP to the container.
+
+Example (``upstart`` on Debian)
+...............................
+
+.. code-block:: bash
+
+   CID=$(docker run -d creack/firefo-vnc)
+   docker run creack/manager:min -a 'Guillaume J. Charmes <guillaume@dotcloud.com>' -d 'Awesome Firefox in VLC' $CID > /etc/init/firefoxvnc.conf
+
+You can now ``start firefoxvnc`` or ``stop firefoxvnc`` and if the container
+dies for some reason, upstart will restart it.
+
+Systemd
+-------
+
+In order to generate a systemd script, we need to use the ``-t``
+option. The generated script will start the container after docker
+daemon. If the container dies, it will respawn.
+``Start/Restart/Reload/Stop`` are supported.
+
+Example (``systemd`` on Fedora)
+...............................
+
+.. code-block:: bash
+
+   CID=$(docker run -d creack/firefo-vnc)
+   docker run creack/manager:min -t systemd -a 'Guillaume J. Charmes <guillaume@dotcloud.com>' -d 'Awesome Firefox in VLC' $CID > /usr/lib/systemd/system/firefoxvnc.service
+
+You can now run ``systemctl start firefoxvnc`` or ``systemctl stop
+firefoxvnc`` and if the container dies for some reason, ``systemd``
+will restart it.

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików