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/
 .hg/
 .git/
+vendor/pkg/

+ 2 - 0
AUTHORS

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

+ 29 - 0
CHANGELOG.md

@@ -1,5 +1,34 @@
 # 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)
 - Runtime: Add cleanup of container when Start() fails
 - 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 git
 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
+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
 run	apt-get install -y -q ruby1.9.3 rubygems libffi-dev

+ 2 - 3
NOTICE

@@ -1,8 +1,7 @@
 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
 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,
 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
 func parseForm(r *http.Request) error {
+	if r == nil {
+		return nil
+	}
 	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
 		return err
 	}
@@ -69,12 +72,12 @@ func httpError(w http.ResponseWriter, err error) {
 		statusCode = http.StatusUnauthorized
 	} else if strings.Contains(err.Error(), "hasn't been activated") {
 		statusCode = http.StatusForbidden
-	}	
-	
+	}
+
 	if err != nil {
 		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 {
@@ -135,8 +138,23 @@ func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
+	if err := parseForm(r); err != nil {
+		return err
+	}
 	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
 	}
 	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 {
+	if err := parseForm(r); err != nil {
+		return nil
+	}
 	config := &Config{}
 	out := &APIRun{}
+	name := r.Form.Get("name")
 
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
 		return err
@@ -515,16 +537,19 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
 		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))
 		config.Dns = defaultDns
 	}
 
-	id, err := srv.ContainerCreate(config)
+	id, warnings, err := srv.ContainerCreate(config, name)
 	if err != nil {
 		return err
 	}
 	out.ID = id
+	for _, warning := range warnings {
+		out.Warnings = append(out.Warnings, warning)
+	}
 
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
 		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")
 	}
 	name := vars["name"]
+
 	removeVolume, err := getBoolParam(r.Form.Get("v"))
 	if err != nil {
 		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
 	}
 	w.WriteHeader(http.StatusNoContent)
@@ -622,6 +652,10 @@ func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r
 		return fmt.Errorf("Missing parameter")
 	}
 	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 {
 		return err
 	}
@@ -655,6 +689,7 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
 		return fmt.Errorf("Missing parameter")
 	}
 	name := vars["name"]
+
 	status, err := srv.ContainerWait(name)
 	if err != nil {
 		return err
@@ -975,7 +1010,7 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s
 		if err != nil {
 			version = APIVERSION
 		}
-		if srv.enableCors {
+		if srv.runtime.config.EnableCors {
 			writeCorsHeaders(w, r)
 		}
 

+ 9 - 17
api_params.go

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

+ 42 - 27
api_test.go

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

+ 4 - 1
auth/auth_test.go

@@ -76,7 +76,7 @@ func TestCreateAccount(t *testing.T) {
 }
 
 func setupTempConfigFile() (*ConfigFile, error) {
-	root, err := ioutil.TempDir("", "docker-test")
+	root, err := ioutil.TempDir("", "docker-test-auth")
 	if err != nil {
 		return nil, err
 	}
@@ -101,6 +101,7 @@ func TestSameAuthDataPostSave(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
+	defer os.RemoveAll(configFile.rootPath)
 
 	err = SaveConfig(configFile)
 	if err != nil {
@@ -127,6 +128,7 @@ func TestResolveAuthConfigIndexServer(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
+	defer os.RemoveAll(configFile.rootPath)
 
 	for _, registry := range []string{"", IndexServerAddress()} {
 		resolved := configFile.ResolveAuthConfig(registry)
@@ -141,6 +143,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
+	defer os.RemoveAll(configFile.rootPath)
 
 	registryAuth := AuthConfig{
 		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 {
+	if strings.Contains(args, ":") {
+		return fmt.Errorf("EXPOSE cannot be used to bind to a host ip or port")
+	}
 	ports := strings.Split(args, " ")
 	b.config.PortSpecs = append(ports, b.config.PortSpecs...)
 	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
 	// Create the container and start it
-	container, err := b.runtime.Create(b.config)
+	container, _, err := b.runtime.Create(b.config, "")
 	if err != nil {
 		return err
 	}
@@ -367,7 +370,7 @@ func (b *buildFile) run() (string, error) {
 	b.config.Image = b.image
 
 	// Create the container and start it
-	c, err := b.runtime.Create(b.config)
+	c, _, err := b.runtime.Create(b.config, "")
 	if err != nil {
 		return "", err
 	}
@@ -378,15 +381,22 @@ func (b *buildFile) run() (string, error) {
 	c.Path = b.config.Cmd[0]
 	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
 	hostConfig := &HostConfig{}
 	if err := c.Start(hostConfig); err != nil {
 		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
 		}
 	}
@@ -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 {
 			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 {
 	if srv == nil {
-		runtime, err := newTestRuntime()
-		if err != nil {
-			t.Fatal(err)
-		}
+		runtime := mkRuntime(t)
 		defer nuke(runtime)
 
 		srv = &Server{
@@ -370,10 +367,7 @@ func TestBuildEntrypoint(t *testing.T) {
 // testing #1405 - config.Cmd does not get cleaned up if
 // utilizing cache
 func TestBuildEntrypointRunCleanup(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
 	srv := &Server{
@@ -402,10 +396,7 @@ func TestBuildEntrypointRunCleanup(t *testing.T) {
 }
 
 func TestBuildImageWithCache(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
 	srv := &Server{
@@ -433,10 +424,7 @@ func TestBuildImageWithCache(t *testing.T) {
 }
 
 func TestBuildImageWithoutCache(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
 	srv := &Server{
@@ -464,10 +452,7 @@ func TestBuildImageWithoutCache(t *testing.T) {
 }
 
 func TestForbiddenContextPath(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
 	srv := &Server{
@@ -513,10 +498,7 @@ func TestForbiddenContextPath(t *testing.T) {
 }
 
 func TestBuildADDFileNotFound(t *testing.T) {
-	runtime, err := newTestRuntime()
-	if err != nil {
-		t.Fatal(err)
-	}
+	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
 	srv := &Server{

+ 253 - 113
commands.go

@@ -23,6 +23,7 @@ import (
 	"os/signal"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"runtime"
 	"sort"
 	"strconv"
@@ -41,9 +42,13 @@ var (
 	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:])
-	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 {
@@ -55,14 +60,7 @@ func ParseCommands(proto, addr string, args ...string) error {
 			fmt.Println("Error: Command not found:", args[0])
 			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...)
 }
@@ -73,10 +71,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 		if !exists {
 			fmt.Fprintf(cli.err, "Error: Command not found: %s\n", args[0])
 		} else {
-			method.Func.CallSlice([]reflect.Value{
-				reflect.ValueOf(cli),
-				reflect.ValueOf([]string{"--help"}),
-			})[0].Interface()
+			method("--help")
 			return nil
 		}
 	}
@@ -99,7 +94,6 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 		{"login", "Register or Login to the docker registry server"},
 		{"logs", "Fetch the logs of a container"},
 		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
-		{"top", "Lookup the running processes of a container"},
 		{"ps", "List containers"},
 		{"pull", "Pull an image or a repository from 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"},
 		{"stop", "Stop a running container"},
 		{"tag", "Tag an image into a repository"},
+		{"top", "Lookup the running processes of a container"},
 		{"version", "Show the docker version information"},
 		{"wait", "Block until a container stops, then print its exit code"},
 	} {
@@ -545,8 +540,23 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
 	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 {
 	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 {
 		return nil
 	}
@@ -555,17 +565,75 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 		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
-	for _, name := range args {
+	for _, name := range cmd.Args() {
 		_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
 		if err != nil {
-			fmt.Fprintf(cli.err, "%s\n", err)
-			encounteredError = fmt.Errorf("Error: failed to start one or more containers")
+			if !*attach || !*openStdin {
+				fmt.Fprintf(cli.err, "%s\n", err)
+				encounteredError = fmt.Errorf("Error: failed to start one or more containers")
+			}
 		} else {
-			fmt.Fprintf(cli.out, "%s\n", name)
+			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 {
@@ -577,30 +645,39 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 		cmd.Usage()
 		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)
 		if err != nil {
 			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
 			if err != nil {
 				fmt.Fprintf(cli.err, "No such image or container: %s\n", name)
+				status = 1
 				continue
 			}
 		}
 
-		indented := new(bytes.Buffer)
 		if err = json.Indent(indented, obj, "", "    "); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
+			status = 1
 			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, "]")
+	if status != 0 {
+		return &utils.StatusError{Status: status}
+	}
 	return nil
 }
 
@@ -647,11 +724,11 @@ func (cli *DockerCli) CmdPort(args ...string) error {
 	}
 
 	port := cmd.Arg(1)
-	proto := "Tcp"
+	proto := "tcp"
 	parts := strings.SplitN(port, "/", 2)
 	if len(parts) == 2 && len(parts[1]) != 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)
 	if err != nil {
@@ -663,8 +740,14 @@ func (cli *DockerCli) CmdPort(args ...string) error {
 		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 {
 		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 {
 	cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
 	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 {
 		return nil
 	}
@@ -751,6 +836,9 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 	if *v {
 		val.Set("v", "1")
 	}
+	if *link {
+		val.Set("link", "1")
+	}
 	for _, name := range cmd.Args() {
 		_, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
 		if err != nil {
@@ -985,25 +1073,19 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 				out.Tag = "<none>"
 			}
 
+			if !*noTrunc {
+				out.ID = utils.TruncateID(out.ID)
+			}
+
 			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 {
 					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.Size), utils.HumanSize(out.VirtualSize))
 				} else {
 					fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size))
 				}
 			} 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 {
 	result := []string{}
 	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 {
-			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)
@@ -1073,7 +1155,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 	}
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	if !*quiet {
-		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS")
+		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
 		if *size {
 			fmt.Fprintln(w, "\tSIZE")
 		} else {
@@ -1082,12 +1164,20 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 	}
 
 	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 *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 out.SizeRootFs > 0 {
 					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")
 			}
 		} 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()
 		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 nil
 }
 
 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 {
 		return nil
 	}
@@ -1245,8 +1334,8 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		cmd.Usage()
 		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 {
 		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")
 	}
 
-	if container.Config.Tty {
+	if container.Config.Tty && cli.isTerminal {
 		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.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("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 nil
@@ -1326,18 +1425,6 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 // Ports type - Used to parse multiple -p flags
 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
 type AttachOpts map[string]bool
 
@@ -1436,6 +1523,13 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	flRm := cmd.Lookup("rm")
 	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
 	if len(hostConfig.ContainerIDFile) > 0 {
 		if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil {
@@ -1447,9 +1541,14 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		}
 		defer containerIDFile.Close()
 	}
+	containerValues := url.Values{}
+	name := flName.Value.String()
+	if name != "" {
+		containerValues.Set("name", name)
+	}
 
 	//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 statusCode == 404 {
 		_, tag := utils.ParseRepositoryTag(config.Image)
@@ -1490,7 +1589,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		if err != nil {
 			return err
 		}
-		body, _, err = cli.call("POST", "/containers/create", config)
+		body, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config)
 		if err != nil {
 			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 {
 		// 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.Tty {
-			if err := cli.monitorTtySize(runResult.ID); err != nil {
-				utils.Errorf("Error monitoring TTY size: %s\n", err)
-			}
-		}
 
 		v := url.Values{}
-		v.Set("logs", "1")
 		v.Set("stream", "1")
 		var out, stderr io.Writer
+		var in io.ReadCloser
 
 		if config.AttachStdin {
 			v.Set("stdin", "1")
+			in = cli.in
 		}
 		if config.AttachStdout {
 			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)
 			return err
 		}
@@ -1579,13 +1697,19 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		// Detached mode
 		<-wait
 	} else {
-		status, err := getExitCode(cli, runResult.ID)
+		running, status, err := getExitCode(cli, runResult.ID)
 		if err != nil {
 			return err
 		}
 		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
 			}
 		}
@@ -1642,6 +1766,10 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
 		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)
 	if err != nil {
 		return nil, -1, err
@@ -1670,6 +1798,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
 		return nil, -1, err
 	}
 	defer resp.Body.Close()
+
 	body, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
 		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 {
 		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)
 	if err != nil {
 		return err
@@ -1742,7 +1876,10 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
 	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)
 	if err != nil {
@@ -1768,6 +1905,10 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
 	rwc, br := clientconn.Hijack()
 	defer rwc.Close()
 
+	if started != nil {
+		started <- true
+	}
+
 	var receiveStdout chan error
 
 	if stdout != nil {
@@ -1854,9 +1995,6 @@ func (cli *DockerCli) resizeTty(id string) {
 }
 
 func (cli *DockerCli) monitorTtySize(id string) error {
-	if !cli.isTerminal {
-		return fmt.Errorf("Impossible to monitor size on non-tty")
-	}
 	cli.resizeTty(id)
 
 	sigchan := make(chan os.Signal, 1)
@@ -1904,20 +2042,22 @@ func waitForExit(cli *DockerCli, containerId string) (int, error) {
 	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)
 	if err != nil {
 		// If we can't connect, then the daemon probably died.
 		if err != ErrConnectionRefused {
-			return -1, err
+			return false, -1, err
 		}
-		return -1, nil
+		return false, -1, nil
 	}
 	c := &Container{}
 	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 {

+ 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
+
+		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
@@ -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
+
+		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
@@ -146,10 +174,24 @@ func TestRunWorkdirExists(t *testing.T) {
 		}
 	})
 
+	container := globalRuntime.List()[0]
+
 	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
 		<-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) {
@@ -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
 		// fact that CmdRun returns.
 		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)
@@ -393,12 +435,14 @@ func TestRunDetach(t *testing.T) {
 	container := globalRuntime.List()[0]
 
 	setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
-		stdinPipe.Write([]byte{'', ''})
+		stdinPipe.Write([]byte{16, 17})
 		if err := stdinPipe.Close(); err != nil {
 			t.Fatal(err)
 		}
 	})
 
+	closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
+
 	// wait for CmdRun to return
 	setTimeout(t, "Waiting for CmdRun timed out", 15*time.Second, func() {
 		<-ch
@@ -411,7 +455,6 @@ func TestRunDetach(t *testing.T) {
 
 	setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() {
 		container.Kill()
-		container.Wait()
 	})
 }
 
@@ -423,39 +466,62 @@ func TestAttachDetach(t *testing.T) {
 	cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
 	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 {
 			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()
 	stdout, stdoutPipe = io.Pipe()
 	cli = NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
 
-	ch := make(chan struct{})
+	ch = make(chan struct{})
 	go func() {
 		defer close(ch)
 		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() {
 		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() {
-		stdinPipe.Write([]byte{'', ''})
+		stdinPipe.Write([]byte{16, 17})
 		if err := stdinPipe.Close(); err != nil {
 			t.Fatal(err)
 		}
 	})
+	closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
 
 	// wait for CmdRun to return
 	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() {
 		container.Kill()
-		container.Wait()
 	})
 }
 
@@ -484,7 +549,7 @@ func TestAttachDisconnect(t *testing.T) {
 	go func() {
 		// Start a process in daemon mode
 		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
 	})
 

+ 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
 
 import (
+	"bytes"
 	"encoding/json"
 	"errors"
 	"flag"
@@ -43,6 +44,7 @@ type Container struct {
 	ResolvConfPath string
 	HostnamePath   string
 	HostsPath      string
+	Name           string
 
 	cmd       *exec.Cmd
 	stdout    *utils.WriteBroadcaster
@@ -58,6 +60,8 @@ type Container struct {
 	// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
 	// Easier than migrating older container configs :)
 	VolumesRW map[string]bool
+
+	activeLinks map[string]*Link
 }
 
 type Config struct {
@@ -70,7 +74,8 @@ type Config struct {
 	AttachStdin     bool
 	AttachStdout    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.
 	OpenStdin       bool // Open stdin
 	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
@@ -90,6 +95,8 @@ type HostConfig struct {
 	Binds           []string
 	ContainerIDFile string
 	LxcConf         []KeyValuePair
+	PortBindings    map[Port][]PortBinding
+	Links           []string
 }
 
 type BindMap struct {
@@ -99,7 +106,11 @@ type BindMap struct {
 }
 
 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 {
@@ -107,6 +118,34 @@ type KeyValuePair struct {
 	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) {
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
 	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")
 	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)")
+	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 {
 		//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)")
 
-	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")
 
-	var flDns ListOpts
+	var flDns utils.ListOpts
 	cmd.Var(&flDns, "dns", "Set custom dns servers")
 
 	flVolumes := NewPathOpts()
 	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")
 
 	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\"")
 
+	var flLinks utils.ListOpts
+	cmd.Var(&flLinks, "link", "Add link to another container (name:alias)")
+
 	if err := cmd.Parse(args); err != nil {
 		return nil, nil, cmd, err
 	}
 	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) {
-		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 len(flAttach) == 0 && !*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
 
 	// 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]
 		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{
-		Hostname:        hostname,
+		Hostname:        *flHostname,
 		Domainname:      domainname,
-		PortSpecs:       flPorts,
+		PortSpecs:       nil, // Deprecated
+		ExposedPorts:    ports,
 		User:            *flUser,
 		Tty:             *flTty,
 		NetworkDisabled: !*flNetwork,
@@ -243,10 +308,13 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 		Privileged:      *flPrivileged,
 		WorkingDir:      *flWorkingDir,
 	}
+
 	hostConfig := &HostConfig{
 		Binds:           binds,
 		ContainerIDFile: *flContainerIDFile,
 		LxcConf:         lxcConf,
+		PortBindings:    portBindings,
+		Links:           flLinks,
 	}
 
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
@@ -261,36 +329,38 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 	return config, hostConfig, cmd, nil
 }
 
-type PortMapping map[string]string
+type PortMapping map[string]string // Deprecated
 
 type NetworkSettings struct {
 	IPAddress   string
 	IPPrefixLen int
 	Gateway     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 {
 	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
 }
@@ -460,11 +530,13 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
 				} else {
 					_, err = io.Copy(cStdin, stdin)
 				}
+				if err == io.ErrClosedPipe {
+					err = nil
+				}
 				if err != nil {
 					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 {
 		container.Config.NetworkDisabled = true
 	} else {
-		if err := container.allocateNetwork(); err != nil {
+		if err := container.allocateNetwork(hostConfig); err != nil {
 			return err
 		}
 	}
@@ -780,6 +852,46 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
 		"-e", "container=lxc",
 		"-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 != "" {
 		workingDir := path.Clean(container.Config.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.SaveHostConfig(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 {
-	hostConfig := &HostConfig{}
-	if err := container.Start(hostConfig); err != nil {
+	if err := container.Start(&HostConfig{}); err != nil {
 		return err
 	}
 	container.Wait()
@@ -882,7 +1025,7 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
 	return utils.NewBufReader(reader), nil
 }
 
-func (container *Container) allocateNetwork() error {
+func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
 	if container.Config.NetworkDisabled {
 		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 {
-		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.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
 	container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
 	container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
 	container.NetworkSettings.Gateway = iface.Gateway.String()
+
 	return nil
 }
 
@@ -951,7 +1117,7 @@ func (container *Container) releaseNetwork() {
 	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 {
 	for {
 		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
@@ -1021,6 +1187,14 @@ func (container *Container) monitor(hostConfig *HostConfig) {
 
 func (container *Container) cleanup() {
 	container.releaseNetwork()
+
+	// Disable all active links
+	if container.activeLinks != nil {
+		for _, link := range container.activeLinks {
+			link.Disable()
+		}
+	}
+
 	if container.Config.OpenStdin {
 		if err := container.stdin.Close(); err != nil {
 			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 {
 		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
 	if err := container.WaitTimeout(10 * time.Second); err != 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 {
 			return err
 		}
 	}
 
-	// Wait for the container to be actually stopped
 	container.Wait()
 	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 {
-	container.State.Lock()
-	defer container.State.Unlock()
 	if !container.State.Running {
 		return nil
 	}
 
 	// 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")
-		if err := container.kill(); err != nil {
+		if err := container.kill(9); err != nil {
 			return err
 		}
 	}
@@ -1099,7 +1276,8 @@ func (container *Container) Stop(seconds int) error {
 	// 2. Wait for the process to exit on its own
 	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)
-		if err := container.kill(); err != nil {
+		// 3. If it doesn't, then send SIGKILL
+		if err := container.Kill(); err != nil {
 			return err
 		}
 	}
@@ -1202,6 +1380,12 @@ func (container *Container) Mounted() (bool, 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())
 }
 
@@ -1292,3 +1476,9 @@ func (container *Container) Copy(resource string) (Archive, error) {
 	}
 	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) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container1, err := runtime.Create(
+	container1, _, err := runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
 		},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -388,11 +389,12 @@ func TestRun(t *testing.T) {
 func TestOutput(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foobar"},
 		},
+		"",
 	)
 	if err != nil {
 		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) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:     GetTestImage(runtime).ID,
 		Cmd:       []string{"cat"},
 		OpenStdin: true,
 		User:      "daemon",
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -471,7 +496,7 @@ func TestCreateVolume(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	c, err := runtime.Create(config)
+	c, _, err := runtime.Create(config, "")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -486,10 +511,11 @@ func TestCreateVolume(t *testing.T) {
 func TestKill(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -530,10 +556,10 @@ func TestExitCode(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	trueContainer, err := runtime.Create(&Config{
+	trueContainer, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true", ""},
-	})
+	}, "")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -545,10 +571,10 @@ func TestExitCode(t *testing.T) {
 		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,
 		Cmd:   []string{"/bin/false", ""},
-	})
+	}, "")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -564,10 +590,11 @@ func TestExitCode(t *testing.T) {
 func TestRestart(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"echo", "-n", "foobar"},
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -594,12 +621,13 @@ func TestRestart(t *testing.T) {
 func TestRestartStdin(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 
 		OpenStdin: true,
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -672,10 +700,11 @@ func TestUser(t *testing.T) {
 	defer nuke(runtime)
 
 	// Default user must be root
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -690,12 +719,13 @@ func TestUser(t *testing.T) {
 	}
 
 	// Set a username
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
 		User: "root",
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -710,12 +740,13 @@ func TestUser(t *testing.T) {
 	}
 
 	// Set a UID
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
 		User: "0",
 	},
+		"",
 	)
 	if err != nil || container.State.ExitCode != 0 {
 		t.Fatal(err)
@@ -730,12 +761,13 @@ func TestUser(t *testing.T) {
 	}
 
 	// Set a different user by uid
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
 		User: "1",
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -752,12 +784,13 @@ func TestUser(t *testing.T) {
 	}
 
 	// Set a different user by username
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
 		User: "daemon",
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -772,12 +805,13 @@ func TestUser(t *testing.T) {
 	}
 
 	// Test an wrong username
-	container, err = runtime.Create(&Config{
+	container, _, err = runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 
 		User: "unknownuser",
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -793,20 +827,22 @@ func TestMultipleContainers(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container1, err := runtime.Create(&Config{
+	container1, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
 	}
 	defer runtime.Destroy(container1)
 
-	container2, err := runtime.Create(&Config{
+	container2, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -847,12 +883,13 @@ func TestMultipleContainers(t *testing.T) {
 func TestStdin(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 
 		OpenStdin: true,
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -892,12 +929,13 @@ func TestStdin(t *testing.T) {
 func TestTty(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 
 		OpenStdin: true,
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -937,10 +975,11 @@ func TestTty(t *testing.T) {
 func TestEnv(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"env"},
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -986,12 +1025,13 @@ func TestEnv(t *testing.T) {
 func TestEntrypoint(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:      GetTestImage(runtime).ID,
 			Entrypoint: []string{"/bin/echo"},
 			Cmd:        []string{"-n", "foobar"},
 		},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1009,11 +1049,12 @@ func TestEntrypoint(t *testing.T) {
 func TestEntrypointNoCmd(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:      GetTestImage(runtime).ID,
 			Entrypoint: []string{"/bin/echo", "foobar"},
 		},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1060,7 +1101,7 @@ func TestLXCConfig(t *testing.T) {
 	cpuMin := 100
 	cpuMax := 10000
 	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true"},
 
@@ -1068,6 +1109,7 @@ func TestLXCConfig(t *testing.T) {
 		Memory:    int64(mem),
 		CpuShares: int64(cpu),
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1084,12 +1126,13 @@ func TestLXCConfig(t *testing.T) {
 func TestCustomLxcConfig(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true"},
 
 		Hostname: "foobar",
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1115,10 +1158,11 @@ func BenchmarkRunSequencial(b *testing.B) {
 	runtime := mkRuntime(b)
 	defer nuke(runtime)
 	for i := 0; i < b.N; i++ {
-		container, err := runtime.Create(&Config{
+		container, _, err := runtime.Create(&Config{
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foo"},
 		},
+			"",
 		)
 		if err != nil {
 			b.Fatal(err)
@@ -1147,10 +1191,11 @@ func BenchmarkRunParallel(b *testing.B) {
 		complete := make(chan error)
 		tasks = append(tasks, complete)
 		go func(i int, complete chan error) {
-			container, err := runtime.Create(&Config{
+			container, _, err := runtime.Create(&Config{
 				Image: GetTestImage(runtime).ID,
 				Cmd:   []string{"echo", "-n", "foo"},
 			},
+				"",
 			)
 			if err != nil {
 				complete <- err
@@ -1189,7 +1234,7 @@ func BenchmarkRunParallel(b *testing.B) {
 }
 
 func tempDir(t *testing.T) string {
-	tmpDir, err := ioutil.TempDir("", "docker-test")
+	tmpDir, err := ioutil.TempDir("", "docker-test-container")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1297,12 +1342,13 @@ func TestBindMounts(t *testing.T) {
 func TestVolumesFromReadonlyMount(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
-	container, err := runtime.Create(
+	container, _, err := runtime.Create(
 		&Config{
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
 			Volumes: map[string]struct{}{"/test": {}},
 		},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1316,12 +1362,13 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
 		t.Fail()
 	}
 
-	container2, err := runtime.Create(
+	container2, _, err := runtime.Create(
 		&Config{
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			VolumesFrom: container.ID,
 		},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1352,11 +1399,12 @@ func TestRestartWithVolumes(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"echo", "-n", "foobar"},
 		Volumes: map[string]struct{}{"/test": {}},
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1395,11 +1443,12 @@ func TestVolumesFromWithVolumes(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Volumes: map[string]struct{}{"/test": {}},
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1422,13 +1471,14 @@ func TestVolumesFromWithVolumes(t *testing.T) {
 		t.Fail()
 	}
 
-	container2, err := runtime.Create(
+	container2, _, err := runtime.Create(
 		&Config{
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"cat", "/test/foo"},
 			VolumesFrom: container.ID,
 			Volumes:     map[string]struct{}{"/test": {}},
 		},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1463,7 +1513,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	c, err := runtime.Create(config)
+	c, _, err := runtime.Create(config, "")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1529,11 +1579,12 @@ func TestMultipleVolumesFrom(t *testing.T) {
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 
-	container, err := runtime.Create(&Config{
+	container, _, err := runtime.Create(&Config{
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Volumes: map[string]struct{}{"/test": {}},
 	},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1556,12 +1607,13 @@ func TestMultipleVolumesFrom(t *testing.T) {
 		t.Fail()
 	}
 
-	container2, err := runtime.Create(
+	container2, _, err := runtime.Create(
 		&Config{
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"sh", "-c", "echo -n bar > /other/foo"},
 			Volumes: map[string]struct{}{"/other": {}},
 		},
+		"",
 	)
 	if err != nil {
 		t.Fatal(err)
@@ -1577,12 +1629,12 @@ func TestMultipleVolumesFrom(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	container3, err := runtime.Create(
+	container3, _, err := runtime.Create(
 		&Config{
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
-		})
+		}, "")
 
 	if err != nil {
 		t.Fatal(err)
@@ -1593,7 +1645,6 @@ func TestMultipleVolumesFrom(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	t.Log(container3.Volumes)
 	if container3.Volumes["/test"] != container.Volumes["/test"] {
 		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
 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 send_slow {1 1}
   spawn pacstrap -c -d -G -i $ROOTFS base haveged --ignore $PKGIGNORE
   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 "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 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.
 rm -rf $ROOTFS

+ 1 - 1
contrib/mkimage-busybox.sh

@@ -35,5 +35,5 @@ do
     cp -a /dev/$X dev
 done
 
-tar -cf- . | docker import - busybox
+tar --numeric-owner -cf- . | docker import - busybox
 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
 
 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.
 rm -rf $ROOTFS

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

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

+ 54 - 26
docker/docker.go

@@ -4,9 +4,11 @@ import (
 	"flag"
 	"fmt"
 	"github.com/dotcloud/docker"
+	"github.com/dotcloud/docker/sysinit"
 	"github.com/dotcloud/docker/utils"
 	"io/ioutil"
 	"log"
+	"net"
 	"os"
 	"os/signal"
 	"strconv"
@@ -22,7 +24,7 @@ var (
 func main() {
 	if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" {
 		// Running in init mode
-		docker.SysInit()
+		sysinit.SysInit()
 		return
 	}
 	// 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.")
 	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
 	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")
+	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()
+
 	if *flVersion {
 		showVersion()
 		return
@@ -46,13 +53,17 @@ func main() {
 		flHosts = flHosts[1:] //trick to display a nice default value in the usage
 	}
 	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 != "" {
-		docker.NetworkBridgeIface = *bridgeName
-	} else {
-		docker.NetworkBridgeIface = docker.DefaultNetworkBridge
+		bridge = *bridgeName
 	}
 	if *flDebug {
 		os.Setenv("DEBUG", "1")
@@ -64,14 +75,31 @@ func main() {
 			flag.Usage()
 			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)
-			os.Exit(-1)
 		}
 	} else {
 		if len(flHosts) > 1 {
 			log.Fatal("Please specify only one -H")
-			return
 		}
 		protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
 		if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
@@ -79,7 +107,6 @@ func main() {
 				os.Exit(sterr.Status)
 			}
 			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)
 	}
-	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)
 	signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
 	go func() {
 		sig := <-c
 		log.Printf("Received signal '%v', exiting\n", sig)
-		removePidFile(pidfile)
+		server.Close()
+		removePidFile(config.Pidfile)
 		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)
 		if protoAddrParts[0] == "unix" {
 			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 /!\\")
 			}
 		} else {
+			server.Close()
+			removePidFile(config.Pidfile)
 			log.Fatal("Invalid protocol format.")
-			os.Exit(-1)
 		}
 		go func() {
 			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
 		if err != nil {
 			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
 maintainer Nick Stinemates
+#
+#    docker build -t docker:docs . && docker run -p 8000:8000 docker:docs
+#
 
 run apt-get update
 run apt-get install -y python-setuptools make
 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
-run pip install -r /docs/requirements.txt
 run cd /docs; make docs
 
 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
 its dependencies. There are two main ways to install this tool:
 
-Native Installation
-...................
+###Native Installation
 
 * Install sphinx: `pip 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`
 * 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
 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
 ``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
 -----
 * 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**:
 
-        .. 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

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

+ 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
 
-	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
 
-           {"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

+ 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
 
-	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
 
-           {"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

+ 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
 
-        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**:
 
     .. 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

+ 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
 
-  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
 ***********************

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

@@ -13,9 +13,12 @@ Docker Remote API v1.6
 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
 ============
@@ -148,6 +151,7 @@ Create a container
 	   }
 	
 	:jsonparam config: the container's configuration
+ 	:query name: container name to use
 	:statuscode 201: no error
 	:statuscode 404: no such container
 	:statuscode 406: impossible to attach (container not running)
@@ -442,7 +446,8 @@ Kill a container
 	.. sourcecode:: http
 
 	   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 404: no such container
 	:statuscode 500: server error
@@ -1131,7 +1136,13 @@ Create a new image from a container's changes
 
     .. 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**:
 
@@ -1147,7 +1158,6 @@ Create a new image from a container's changes
     :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

+ 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
   index_api
   docker_remote_api
-
+  remote_api_client_libraries
 

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

@@ -4,11 +4,8 @@
 
 .. _cli:
 
-Overview
-======================
-
-Docker Usage
-~~~~~~~~~~~~~~~~~~
+Command Line Help
+-----------------
 
 To list available commands, either run ``docker`` with no parameters or execute
 ``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
-:description: -- todo: change me
-:keywords: todo, commands, command line, help, docker, documentation
+:description: docker command line interface
+:keywords: commands, command line, help, docker
 
 
 Commands
@@ -12,34 +12,3 @@ Contents:
   :maxdepth: 1
 
   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
 ======================
 
-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_couchdb_service`
 * :ref:`postgresql_service`
-* :ref:`mongodb`
+* :ref:`mongodb_image`

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

@@ -1,6 +1,6 @@
 :title: Docker Examples
 :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:
@@ -24,3 +24,4 @@ to more substantial services like you might find in production.
    postgresql_service
    mongodb
    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
 :keywords: containers, lxc, concepts, explanation
 
-.. image:: https://www.docker.io/static/img/linked/dockerlogo-horizontal.png
-
 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
 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,
-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
 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 -"
 
    # 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
    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 -"
 
    # 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
    sudo apt-get update

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

@@ -40,4 +40,4 @@ Docker Github Repo:
 * `CentOS
   <https://github.com/dotcloud/docker/blob/master/contrib/mkimage-centos.sh>`_
 * `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
   sudo service docker restart
 
+.. _bind_docker:
+
 Bind Docker to another host/port or a Unix socket
 -------------------------------------------------
 
@@ -142,7 +144,7 @@ Expose a service on a TCP port
 .. code-block:: bash
 
   # 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?
   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).
 * If ``<src>`` is a directory, the entire directory is copied,
   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.
 
   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