瀏覽代碼

bump to master again

Victor Vieux 12 年之前
父節點
當前提交
bf63cb9045
共有 67 個文件被更改,包括 1423 次插入976 次删除
  1. 1 0
      AUTHORS
  2. 14 0
      CHANGELOG.md
  3. 79 46
      api.go
  4. 35 35
      api_params.go
  5. 75 75
      api_test.go
  6. 3 0
      archive.go
  7. 4 4
      auth/auth.go
  8. 5 5
      builder.go
  9. 20 20
      builder_client.go
  10. 71 84
      buildfile.go
  11. 3 3
      buildfile_test.go
  12. 1 1
      changes.go
  13. 102 99
      commands.go
  14. 46 45
      container.go
  15. 64 33
      container_test.go
  16. 3 3
      contrib/crashTest.go
  17. 8 14
      contrib/mkimage-debian.sh
  18. 2 2
      docker/docker.go
  19. 5 1
      docs/Makefile
  20. 11 9
      docs/README.md
  21. 59 13
      docs/sources/api/docker_remote_api.rst
  22. 2 2
      docs/sources/api/index.rst
  23. 0 1
      docs/sources/api/registry_api.rst
  24. 21 6
      docs/sources/commandline/command/build.rst
  25. 2 2
      docs/sources/concepts/introduction.rst
  26. 2 2
      docs/sources/contributing/devenvironment.rst
  27. 2 2
      docs/sources/examples/couchdb_data_volumes.rst
  28. 2 1
      docs/sources/examples/index.rst
  29. 236 0
      docs/sources/examples/nodejs_web_app.rst
  30. 2 2
      docs/sources/examples/python_web_app.rst
  31. 1 1
      docs/sources/examples/running_examples.rst
  32. 3 3
      docs/sources/examples/running_redis_service.rst
  33. 49 4
      docs/sources/examples/running_ssh_service.rst
  34. 3 4
      docs/sources/faq.rst
  35. 2 2
      docs/sources/index.rst
  36. 10 0
      docs/sources/installation/ubuntulinux.rst
  37. 1 0
      docs/sources/installation/windows.rst
  38. 2 2
      docs/sources/use/basics.rst
  39. 8 2
      docs/sources/use/builder.rst
  40. 4 4
      docs/sources/use/workingwithrepository.rst
  41. 8 8
      docs/website/gettingstarted/index.html
  42. 1 1
      docs/website/index.html
  43. 24 22
      graph.go
  44. 11 11
      graph_test.go
  45. 14 6
      hack/dockerbuilder/Dockerfile
  46. 2 1
      hack/dockerbuilder/dockerbuilder
  47. 0 3
      hack/dockerbuilder/fake_initctl
  48. 2 0
      hack/infrastructure/MAINTAINERS
  49. 5 0
      hack/infrastructure/README.md
  50. 18 18
      image.go
  51. 1 1
      lxc_template.go
  52. 7 8
      network.go
  53. 1 1
      network_test.go
  54. 11 0
      packaging/debian/changelog
  55. 19 0
      packaging/ubuntu/changelog
  56. 1 2
      packaging/ubuntu/docker.upstart
  57. 41 27
      registry/registry.go
  58. 31 31
      runtime.go
  59. 26 26
      runtime_test.go
  60. 104 100
      server.go
  61. 2 2
      server_test.go
  62. 13 13
      tags.go
  63. 2 0
      term/MAINTAINERS
  64. 7 105
      term/term.go
  65. 27 5
      term/termios_darwin.go
  66. 22 36
      term/termios_linux.go
  67. 60 17
      utils/utils.go

+ 1 - 0
AUTHORS

@@ -15,6 +15,7 @@ Brian McCallister <brianm@skife.org>
 Bruno Bigras <bigras.bruno@gmail.com>
 Bruno Bigras <bigras.bruno@gmail.com>
 Caleb Spare <cespare@gmail.com>
 Caleb Spare <cespare@gmail.com>
 Charles Hooper <charles.hooper@dotcloud.com>
 Charles Hooper <charles.hooper@dotcloud.com>
+Daniel Gasienica <daniel@gasienica.ch>
 Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
 Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
 Daniel Robinson <gottagetmac@gmail.com>
 Daniel Robinson <gottagetmac@gmail.com>
 Daniel Von Fange <daniel@leancoder.com>
 Daniel Von Fange <daniel@leancoder.com>

+ 14 - 0
CHANGELOG.md

@@ -1,5 +1,19 @@
 # Changelog
 # Changelog
 
 
+## 0.4.0 (2013-06-03)
+ + Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
+ + Introducing Remote API: control Docker programmatically using a simple HTTP/json API
+ * Runtime: various reliability and usability improvements
+
+## 0.3.4 (2013-05-30)
+ + Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
+ + Builder: 'docker build -t FOO' applies the tag FOO to the newly built container.
+ + Runtime: interactive TTYs correctly handle window resize
+ * Runtime: fix how configuration is merged between layers
+ + Remote API: split stdout and stderr on 'docker run'
+ + Remote API: optionally listen on a different IP and port (use at your own risk)
+ * Documentation: improved install instructions.
+
 ## 0.3.3 (2013-05-23)
 ## 0.3.3 (2013-05-23)
  - Registry: Fix push regression
  - Registry: Fix push regression
  - Various bugfixes
  - Various bugfixes

+ 79 - 46
api.go

@@ -13,7 +13,7 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-const API_VERSION = 1.1
+const APIVERSION = 1.1
 
 
 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
 	conn, _, err := w.(http.Hijacker).Hijack()
 	conn, _, err := w.(http.Hijacker).Hijack()
@@ -45,12 +45,14 @@ func httpError(w http.ResponseWriter, err error) {
 		http.Error(w, err.Error(), http.StatusNotFound)
 		http.Error(w, err.Error(), http.StatusNotFound)
 	} else if strings.HasPrefix(err.Error(), "Bad parameter") {
 	} else if strings.HasPrefix(err.Error(), "Bad parameter") {
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		http.Error(w, err.Error(), http.StatusBadRequest)
+	} else if strings.HasPrefix(err.Error(), "Impossible") {
+		http.Error(w, err.Error(), http.StatusNotAcceptable)
 	} else {
 	} else {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 	}
 	}
 }
 }
 
 
-func writeJson(w http.ResponseWriter, b []byte) {
+func writeJSON(w http.ResponseWriter, b []byte) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 	w.Write(b)
 	w.Write(b)
 }
 }
@@ -80,7 +82,7 @@ func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -109,11 +111,11 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque
 	}
 	}
 
 
 	if status != "" {
 	if status != "" {
-		b, err := json.Marshal(&ApiAuth{Status: status})
+		b, err := json.Marshal(&APIAuth{Status: status})
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		writeJson(w, b)
+		writeJSON(w, b)
 		return nil
 		return nil
 	}
 	}
 	w.WriteHeader(http.StatusNoContent)
 	w.WriteHeader(http.StatusNoContent)
@@ -126,7 +128,7 @@ func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Req
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -155,7 +157,7 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r
 	return nil
 	return nil
 }
 }
 
 
-func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 	if err := parseForm(r); err != nil {
 		return err
 		return err
 	}
 	}
@@ -174,7 +176,7 @@ func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -191,7 +193,7 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -208,7 +210,7 @@ func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *ht
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -225,11 +227,11 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
-func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 	if err := parseForm(r); err != nil {
 		return err
 		return err
 	}
 	}
@@ -249,7 +251,7 @@ func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *h
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -292,12 +294,12 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	b, err := json.Marshal(&ApiId{id})
+	b, err := json.Marshal(&APIID{id})
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	w.WriteHeader(http.StatusCreated)
 	w.WriteHeader(http.StatusCreated)
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -312,16 +314,25 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht
 	tag := r.Form.Get("tag")
 	tag := r.Form.Get("tag")
 	repo := r.Form.Get("repo")
 	repo := r.Form.Get("repo")
 
 
+	if version > 1.0 {
+		w.Header().Set("Content-Type", "application/json")
+	}
+	sf := utils.NewStreamFormatter(version > 1.0)
 	if image != "" { //pull
 	if image != "" { //pull
 		registry := r.Form.Get("registry")
 		registry := r.Form.Get("registry")
-		if version > 1.0 {
-			w.Header().Set("Content-Type", "application/json")
-		}
-		if err := srv.ImagePull(image, tag, registry, w, version > 1.0); err != nil {
+		if err := srv.ImagePull(image, tag, registry, w, sf); err != nil {
+			if sf.Used() {
+				w.Write(sf.FormatError(err))
+				return nil
+			}
 			return err
 			return err
 		}
 		}
 	} else { //import
 	} else { //import
-		if err := srv.ImageImport(src, repo, tag, r.Body, w); err != nil {
+		if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil {
+			if sf.Used() {
+				w.Write(sf.FormatError(err))
+				return nil
+			}
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -342,7 +353,7 @@ func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *htt
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -357,16 +368,22 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
 	name := vars["name"]
 	name := vars["name"]
-
-	imgId, err := srv.ImageInsert(name, url, path, w)
+	if version > 1.0 {
+		w.Header().Set("Content-Type", "application/json")
+	}
+	sf := utils.NewStreamFormatter(version > 1.0)
+	imgID, err := srv.ImageInsert(name, url, path, w, sf)
 	if err != nil {
 	if err != nil {
-		return err
+		if sf.Used() {
+			w.Write(sf.FormatError(err))
+			return nil
+		}
 	}
 	}
-	b, err := json.Marshal(&ApiId{Id: imgId})
+	b, err := json.Marshal(&APIID{ID: imgID})
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -380,8 +397,15 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
 	name := vars["name"]
 	name := vars["name"]
-
-	if err := srv.ImagePush(name, registry, w); err != nil {
+	if version > 1.0 {
+		w.Header().Set("Content-Type", "application/json")
+	}
+	sf := utils.NewStreamFormatter(version > 1.0)
+	if err := srv.ImagePush(name, registry, w, sf); err != nil {
+		if sf.Used() {
+			w.Write(sf.FormatError(err))
+			return nil
+		}
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -397,8 +421,8 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
 		return err
 		return err
 	}
 	}
 
 
-	out := &ApiRun{
-		Id: id,
+	out := &APIRun{
+		ID: id,
 	}
 	}
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
 		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
 		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
@@ -413,7 +437,7 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
 		return err
 		return err
 	}
 	}
 	w.WriteHeader(http.StatusCreated)
 	w.WriteHeader(http.StatusCreated)
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -510,11 +534,11 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	b, err := json.Marshal(&ApiWait{StatusCode: status})
+	b, err := json.Marshal(&APIWait{StatusCode: status})
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -601,7 +625,7 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -619,17 +643,17 @@ func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *htt
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
 func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
-	apiConfig := &ApiImageConfig{}
+	apiConfig := &APIImageConfig{}
 	if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
 	if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	image, err := srv.ImageGetCached(apiConfig.Id, apiConfig.Config)
+	image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -637,12 +661,12 @@ func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *
 		w.WriteHeader(http.StatusNotFound)
 		w.WriteHeader(http.StatusNotFound)
 		return nil
 		return nil
 	}
 	}
-	apiId := &ApiId{Id: image.Id}
-	b, err := json.Marshal(apiId)
+	apiID := &APIID{ID: image.ID}
+	b, err := json.Marshal(apiID)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	writeJson(w, b)
+	writeJSON(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -650,6 +674,13 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
 	if err := r.ParseMultipartForm(4096); err != nil {
 	if err := r.ParseMultipartForm(4096); err != nil {
 		return err
 		return err
 	}
 	}
+	remote := r.FormValue("t")
+	tag := ""
+	if strings.Contains(remote, ":") {
+		remoteParts := strings.Split(remote, ":")
+		tag = remoteParts[1]
+		remote = remoteParts[0]
+	}
 
 
 	dockerfile, _, err := r.FormFile("Dockerfile")
 	dockerfile, _, err := r.FormFile("Dockerfile")
 	if err != nil {
 	if err != nil {
@@ -664,8 +695,10 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
 	}
 	}
 
 
 	b := NewBuildFile(srv, utils.NewWriteFlusher(w))
 	b := NewBuildFile(srv, utils.NewWriteFlusher(w))
-	if _, err := b.Build(dockerfile, context); err != nil {
+	if id, err := b.Build(dockerfile, context); err != nil {
 		fmt.Fprintf(w, "Error build: %s\n", err)
 		fmt.Fprintf(w, "Error build: %s\n", err)
+	} else if remote != "" {
+		srv.runtime.repositories.Set(remote, tag, id, false)
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -679,13 +712,13 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
 			"/auth":                         getAuth,
 			"/auth":                         getAuth,
 			"/version":                      getVersion,
 			"/version":                      getVersion,
 			"/info":                         getInfo,
 			"/info":                         getInfo,
-			"/images/json":                  getImagesJson,
+			"/images/json":                  getImagesJSON,
 			"/images/viz":                   getImagesViz,
 			"/images/viz":                   getImagesViz,
 			"/images/search":                getImagesSearch,
 			"/images/search":                getImagesSearch,
 			"/images/{name:.*}/history":     getImagesHistory,
 			"/images/{name:.*}/history":     getImagesHistory,
 			"/images/{name:.*}/json":        getImagesByName,
 			"/images/{name:.*}/json":        getImagesByName,
-			"/containers/ps":                getContainersJson,
-			"/containers/json":              getContainersJson,
+			"/containers/ps":                getContainersJSON,
+			"/containers/json":              getContainersJSON,
 			"/containers/{name:.*}/export":  getContainersExport,
 			"/containers/{name:.*}/export":  getContainersExport,
 			"/containers/{name:.*}/changes": getContainersChanges,
 			"/containers/{name:.*}/changes": getContainersChanges,
 			"/containers/{name:.*}/json":    getContainersByName,
 			"/containers/{name:.*}/json":    getContainersByName,
@@ -734,9 +767,9 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
 				}
 				}
 				version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
 				version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
 				if err != nil {
 				if err != nil {
-					version = API_VERSION
+					version = APIVERSION
 				}
 				}
-				if version == 0 || version > API_VERSION {
+				if version == 0 || version > APIVERSION {
 					w.WriteHeader(http.StatusNotFound)
 					w.WriteHeader(http.StatusNotFound)
 					return
 					return
 				}
 				}

+ 35 - 35
api_params.go

@@ -1,75 +1,75 @@
 package docker
 package docker
 
 
-type ApiHistory struct {
-	Id        string
+type APIHistory struct {
+	ID        string `json:"Id"`
 	Created   int64
 	Created   int64
-	CreatedBy string
+	CreatedBy string `json:",omitempty"`
 }
 }
 
 
-type ApiImages struct {
+type APIImages struct {
 	Repository string `json:",omitempty"`
 	Repository string `json:",omitempty"`
 	Tag        string `json:",omitempty"`
 	Tag        string `json:",omitempty"`
-	Id         string
-	Created    int64 `json:",omitempty"`
+	ID         string `json:"Id"`
+	Created    int64
 	Size       int64
 	Size       int64
 	ParentSize int64
 	ParentSize int64
+
 }
 }
 
 
-type ApiInfo struct {
+type APIInfo struct {
+	Debug       bool
 	Containers  int
 	Containers  int
-	Version     string
 	Images      int
 	Images      int
-	Debug       bool
-	GoVersion   string
-	NFd         int `json:",omitempty"`
-	NGoroutines int `json:",omitempty"`
+	NFd         int  `json:",omitempty"`
+	NGoroutines int  `json:",omitempty"`
+	MemoryLimit bool `json:",omitempty"`
+	SwapLimit   bool `json:",omitempty"`
 }
 }
 
 
-type ApiContainers struct {
-	Id         string
-	Image      string `json:",omitempty"`
-	Command    string `json:",omitempty"`
-	Created    int64  `json:",omitempty"`
-	Status     string `json:",omitempty"`
-	Ports      string `json:",omitempty"`
+type APIContainers struct {
+	ID         string `json:"Id"`
+	Image      string 
+	Command    string 
+	Created    int64  
+	Status     string 
+	Ports      string 
 	SizeRw     int64
 	SizeRw     int64
 	SizeRootFs int64
 	SizeRootFs int64
 }
 }
 
 
-type ApiSearch struct {
+type APISearch struct {
 	Name        string
 	Name        string
 	Description string
 	Description string
 }
 }
 
 
-type ApiId struct {
-	Id string
+type APIID struct {
+	ID string `json:"Id"`
 }
 }
 
 
-type ApiRun struct {
-	Id       string
-	Warnings []string
+type APIRun struct {
+	ID       string   `json:"Id"`
+	Warnings []string `json:",omitempty"`
 }
 }
 
 
-type ApiPort struct {
+type APIPort struct {
 	Port string
 	Port string
 }
 }
 
 
-type ApiVersion struct {
-	Version     string
-	GitCommit   string
-	MemoryLimit bool
-	SwapLimit   bool
+type APIVersion struct {
+	Version   string
+	GitCommit string `json:",omitempty"`
+	GoVersion string `json:",omitempty"`
 }
 }
 
 
-type ApiWait struct {
+type APIWait struct {
 	StatusCode int
 	StatusCode int
 }
 }
 
 
-type ApiAuth struct {
+type APIAuth struct {
 	Status string
 	Status string
 }
 }
 
 
-type ApiImageConfig struct {
-	Id string
+type APIImageConfig struct {
+	ID string `json:"Id"`
 	*Config
 	*Config
 }
 }

+ 75 - 75
api_test.go

@@ -37,17 +37,17 @@ func TestGetAuth(t *testing.T) {
 		Email:    "utest@yopmail.com",
 		Email:    "utest@yopmail.com",
 	}
 	}
 
 
-	authConfigJson, err := json.Marshal(authConfig)
+	authConfigJSON, err := json.Marshal(authConfig)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJson))
+	req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJSON))
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := postAuth(srv, API_VERSION, r, req, nil); err != nil {
+	if err := postAuth(srv, APIVERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -73,11 +73,11 @@ func TestGetVersion(t *testing.T) {
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 
 
-	if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil {
+	if err := getVersion(srv, APIVERSION, r, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	v := &ApiVersion{}
+	v := &APIVersion{}
 	if err = json.Unmarshal(r.Body.Bytes(), v); err != nil {
 	if err = json.Unmarshal(r.Body.Bytes(), v); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -97,21 +97,21 @@ func TestGetInfo(t *testing.T) {
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 
 
-	if err := getInfo(srv, API_VERSION, r, nil, nil); err != nil {
+	if err := getInfo(srv, APIVERSION, r, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	infos := &ApiInfo{}
+	infos := &APIInfo{}
 	err = json.Unmarshal(r.Body.Bytes(), infos)
 	err = json.Unmarshal(r.Body.Bytes(), infos)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if infos.Version != VERSION {
-		t.Errorf("Excepted version %s, %s found", VERSION, infos.Version)
+	if infos.Images != 1 {
+		t.Errorf("Excepted images: %d, %d found", 1, infos.Images)
 	}
 	}
 }
 }
 
 
-func TestGetImagesJson(t *testing.T) {
+func TestGetImagesJSON(t *testing.T) {
 	runtime, err := newTestRuntime()
 	runtime, err := newTestRuntime()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -128,11 +128,11 @@ func TestGetImagesJson(t *testing.T) {
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 
 
-	if err := getImagesJson(srv, API_VERSION, r, req, nil); err != nil {
+	if err := getImagesJSON(srv, APIVERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	images := []ApiImages{}
+	images := []APIImages{}
 	if err := json.Unmarshal(r.Body.Bytes(), &images); err != nil {
 	if err := json.Unmarshal(r.Body.Bytes(), &images); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -153,11 +153,11 @@ func TestGetImagesJson(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := getImagesJson(srv, API_VERSION, r2, req2, nil); err != nil {
+	if err := getImagesJSON(srv, APIVERSION, r2, req2, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	images2 := []ApiImages{}
+	images2 := []APIImages{}
 	if err := json.Unmarshal(r2.Body.Bytes(), &images2); err != nil {
 	if err := json.Unmarshal(r2.Body.Bytes(), &images2); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -166,8 +166,8 @@ func TestGetImagesJson(t *testing.T) {
 		t.Errorf("Excepted 1 image, %d found", len(images2))
 		t.Errorf("Excepted 1 image, %d found", len(images2))
 	}
 	}
 
 
-	if images2[0].Id != GetTestImage(runtime).Id {
-		t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).Id, images2[0].Id)
+	if images2[0].ID != GetTestImage(runtime).ID {
+		t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).ID, images2[0].ID)
 	}
 	}
 
 
 	r3 := httptest.NewRecorder()
 	r3 := httptest.NewRecorder()
@@ -178,11 +178,11 @@ func TestGetImagesJson(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := getImagesJson(srv, API_VERSION, r3, req3, nil); err != nil {
+	if err := getImagesJSON(srv, APIVERSION, r3, req3, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	images3 := []ApiImages{}
+	images3 := []APIImages{}
 	if err := json.Unmarshal(r3.Body.Bytes(), &images3); err != nil {
 	if err := json.Unmarshal(r3.Body.Bytes(), &images3); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -199,7 +199,7 @@ func TestGetImagesJson(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	err = getImagesJson(srv, API_VERSION, r4, req4, nil)
+	err = getImagesJSON(srv, APIVERSION, r4, req4, nil)
 	if err == nil {
 	if err == nil {
 		t.Fatalf("Error expected, received none")
 		t.Fatalf("Error expected, received none")
 	}
 	}
@@ -220,7 +220,7 @@ func TestGetImagesViz(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getImagesViz(srv, API_VERSION, r, nil, nil); err != nil {
+	if err := getImagesViz(srv, APIVERSION, r, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -256,11 +256,11 @@ func TestGetImagesSearch(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := getImagesSearch(srv, API_VERSION, r, req, nil); err != nil {
+	if err := getImagesSearch(srv, APIVERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	results := []ApiSearch{}
+	results := []APISearch{}
 	if err := json.Unmarshal(r.Body.Bytes(), &results); err != nil {
 	if err := json.Unmarshal(r.Body.Bytes(), &results); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -280,11 +280,11 @@ func TestGetImagesHistory(t *testing.T) {
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 
 
-	if err := getImagesHistory(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
+	if err := getImagesHistory(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	history := []ApiHistory{}
+	history := []APIHistory{}
 	if err := json.Unmarshal(r.Body.Bytes(), &history); err != nil {
 	if err := json.Unmarshal(r.Body.Bytes(), &history); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -303,7 +303,7 @@ func TestGetImagesByName(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getImagesByName(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
+	if err := getImagesByName(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -311,12 +311,12 @@ func TestGetImagesByName(t *testing.T) {
 	if err := json.Unmarshal(r.Body.Bytes(), img); err != nil {
 	if err := json.Unmarshal(r.Body.Bytes(), img); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if img.Id != GetTestImage(runtime).Id || img.Comment != "Imported from http://get.docker.io/images/busybox" {
+	if img.ID != GetTestImage(runtime).ID || img.Comment != "Imported from http://get.docker.io/images/busybox" {
 		t.Errorf("Error inspecting image")
 		t.Errorf("Error inspecting image")
 	}
 	}
 }
 }
 
 
-func TestGetContainersJson(t *testing.T) {
+func TestGetContainersJSON(t *testing.T) {
 	runtime, err := newTestRuntime()
 	runtime, err := newTestRuntime()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -326,7 +326,7 @@ func TestGetContainersJson(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"echo", "test"},
 		Cmd:   []string{"echo", "test"},
 	})
 	})
 	if err != nil {
 	if err != nil {
@@ -340,18 +340,18 @@ func TestGetContainersJson(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getContainersJson(srv, API_VERSION, r, req, nil); err != nil {
+	if err := getContainersJSON(srv, APIVERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	containers := []ApiContainers{}
+	containers := []APIContainers{}
 	if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
 	if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if len(containers) != 1 {
 	if len(containers) != 1 {
 		t.Fatalf("Excepted %d container, %d found", 1, len(containers))
 		t.Fatalf("Excepted %d container, %d found", 1, len(containers))
 	}
 	}
-	if containers[0].Id != container.Id {
-		t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.Id, containers[0].Id)
+	if containers[0].ID != container.ID {
+		t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.ID, containers[0].ID)
 	}
 	}
 }
 }
 
 
@@ -369,7 +369,7 @@ func TestGetContainersExport(t *testing.T) {
 	// Create a container and remove a file
 	// Create a container and remove a file
 	container, err := builder.Create(
 	container, err := builder.Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"touch", "/test"},
 			Cmd:   []string{"touch", "/test"},
 		},
 		},
 	)
 	)
@@ -383,7 +383,7 @@ func TestGetContainersExport(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err = getContainersExport(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err = getContainersExport(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -424,7 +424,7 @@ func TestGetContainersChanges(t *testing.T) {
 	// Create a container and remove a file
 	// Create a container and remove a file
 	container, err := builder.Create(
 	container, err := builder.Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
 		},
 		},
 	)
 	)
@@ -438,7 +438,7 @@ func TestGetContainersChanges(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getContainersChanges(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err := getContainersChanges(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	changes := []Change{}
 	changes := []Change{}
@@ -472,7 +472,7 @@ func TestGetContainersByName(t *testing.T) {
 	// Create a container and remove a file
 	// Create a container and remove a file
 	container, err := builder.Create(
 	container, err := builder.Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "test"},
 			Cmd:   []string{"echo", "test"},
 		},
 		},
 	)
 	)
@@ -482,15 +482,15 @@ func TestGetContainersByName(t *testing.T) {
 	defer runtime.Destroy(container)
 	defer runtime.Destroy(container)
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getContainersByName(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err := getContainersByName(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	outContainer := &Container{}
 	outContainer := &Container{}
 	if err := json.Unmarshal(r.Body.Bytes(), outContainer); err != nil {
 	if err := json.Unmarshal(r.Body.Bytes(), outContainer); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if outContainer.Id != container.Id {
-		t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.Id, outContainer.Id)
+	if outContainer.ID != container.ID {
+		t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.ID, outContainer.ID)
 	}
 	}
 }
 }
 
 
@@ -514,7 +514,7 @@ func TestPostAuth(t *testing.T) {
 	auth.SaveConfig(runtime.root, authStr, config.Email)
 	auth.SaveConfig(runtime.root, authStr, config.Email)
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getAuth(srv, API_VERSION, r, nil, nil); err != nil {
+	if err := getAuth(srv, APIVERSION, r, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -542,7 +542,7 @@ func TestPostCommit(t *testing.T) {
 	// Create a container and remove a file
 	// Create a container and remove a file
 	container, err := builder.Create(
 	container, err := builder.Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"touch", "/test"},
 			Cmd:   []string{"touch", "/test"},
 		},
 		},
 	)
 	)
@@ -555,24 +555,24 @@ func TestPostCommit(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.Id, bytes.NewReader([]byte{}))
+	req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.ID, bytes.NewReader([]byte{}))
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postCommit(srv, API_VERSION, r, req, nil); err != nil {
+	if err := postCommit(srv, APIVERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Code != http.StatusCreated {
 	if r.Code != http.StatusCreated {
 		t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
 		t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
 	}
 	}
 
 
-	apiId := &ApiId{}
-	if err := json.Unmarshal(r.Body.Bytes(), apiId); err != nil {
+	apiID := &APIID{}
+	if err := json.Unmarshal(r.Body.Bytes(), apiID); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if _, err := runtime.graph.Get(apiId.Id); err != nil {
+	if _, err := runtime.graph.Get(apiID.ID); err != nil {
 		t.Fatalf("The image has not been commited")
 		t.Fatalf("The image has not been commited")
 	}
 	}
 }
 }
@@ -715,7 +715,7 @@ func TestPostImagesInsert(t *testing.T) {
 	// 	t.Fatalf("The test file has not been found")
 	// 	t.Fatalf("The test file has not been found")
 	// }
 	// }
 
 
-	// if err := srv.runtime.graph.Delete(img.Id); err != nil {
+	// if err := srv.runtime.graph.Delete(img.ID); err != nil {
 	// 	t.Fatal(err)
 	// 	t.Fatal(err)
 	// }
 	// }
 }
 }
@@ -824,8 +824,8 @@ func TestPostContainersCreate(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	configJson, err := json.Marshal(&Config{
-		Image:  GetTestImage(runtime).Id,
+	configJSON, err := json.Marshal(&Config{
+		Image:  GetTestImage(runtime).ID,
 		Memory: 33554432,
 		Memory: 33554432,
 		Cmd:    []string{"touch", "/test"},
 		Cmd:    []string{"touch", "/test"},
 	})
 	})
@@ -833,25 +833,25 @@ func TestPostContainersCreate(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJson))
+	req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJSON))
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersCreate(srv, API_VERSION, r, req, nil); err != nil {
+	if err := postContainersCreate(srv, APIVERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Code != http.StatusCreated {
 	if r.Code != http.StatusCreated {
 		t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
 		t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
 	}
 	}
 
 
-	apiRun := &ApiRun{}
+	apiRun := &APIRun{}
 	if err := json.Unmarshal(r.Body.Bytes(), apiRun); err != nil {
 	if err := json.Unmarshal(r.Body.Bytes(), apiRun); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	container := srv.runtime.Get(apiRun.Id)
+	container := srv.runtime.Get(apiRun.ID)
 	if container == nil {
 	if container == nil {
 		t.Fatalf("Container not created")
 		t.Fatalf("Container not created")
 	}
 	}
@@ -880,7 +880,7 @@ func TestPostContainersKill(t *testing.T) {
 
 
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image:     GetTestImage(runtime).Id,
+			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
@@ -902,7 +902,7 @@ func TestPostContainersKill(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersKill(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err := postContainersKill(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Code != http.StatusNoContent {
 	if r.Code != http.StatusNoContent {
@@ -924,7 +924,7 @@ func TestPostContainersRestart(t *testing.T) {
 
 
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image:     GetTestImage(runtime).Id,
+			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
@@ -945,12 +945,12 @@ func TestPostContainersRestart(t *testing.T) {
 		t.Errorf("Container should be running")
 		t.Errorf("Container should be running")
 	}
 	}
 
 
-	req, err := http.NewRequest("POST", "/containers/"+container.Id+"/restart?t=1", bytes.NewReader([]byte{}))
+	req, err := http.NewRequest("POST", "/containers/"+container.ID+"/restart?t=1", bytes.NewReader([]byte{}))
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersRestart(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
+	if err := postContainersRestart(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Code != http.StatusNoContent {
 	if r.Code != http.StatusNoContent {
@@ -980,7 +980,7 @@ func TestPostContainersStart(t *testing.T) {
 
 
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image:     GetTestImage(runtime).Id,
+			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
@@ -991,7 +991,7 @@ func TestPostContainersStart(t *testing.T) {
 	defer runtime.Destroy(container)
 	defer runtime.Destroy(container)
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err := postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Code != http.StatusNoContent {
 	if r.Code != http.StatusNoContent {
@@ -1006,7 +1006,7 @@ func TestPostContainersStart(t *testing.T) {
 	}
 	}
 
 
 	r = httptest.NewRecorder()
 	r = httptest.NewRecorder()
-	if err = postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err == nil {
+	if err = postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err == nil {
 		t.Fatalf("A running containter should be able to be started")
 		t.Fatalf("A running containter should be able to be started")
 	}
 	}
 
 
@@ -1026,7 +1026,7 @@ func TestPostContainersStop(t *testing.T) {
 
 
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image:     GetTestImage(runtime).Id,
+			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
@@ -1048,12 +1048,12 @@ func TestPostContainersStop(t *testing.T) {
 	}
 	}
 
 
 	// Note: as it is a POST request, it requires a body.
 	// Note: as it is a POST request, it requires a body.
-	req, err := http.NewRequest("POST", "/containers/"+container.Id+"/stop?t=1", bytes.NewReader([]byte{}))
+	req, err := http.NewRequest("POST", "/containers/"+container.ID+"/stop?t=1", bytes.NewReader([]byte{}))
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersStop(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
+	if err := postContainersStop(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Code != http.StatusNoContent {
 	if r.Code != http.StatusNoContent {
@@ -1075,7 +1075,7 @@ func TestPostContainersWait(t *testing.T) {
 
 
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image:     GetTestImage(runtime).Id,
+			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/sleep", "1"},
 			Cmd:       []string{"/bin/sleep", "1"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
@@ -1091,10 +1091,10 @@ func TestPostContainersWait(t *testing.T) {
 
 
 	setTimeout(t, "Wait timed out", 3*time.Second, func() {
 	setTimeout(t, "Wait timed out", 3*time.Second, func() {
 		r := httptest.NewRecorder()
 		r := httptest.NewRecorder()
-		if err := postContainersWait(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
+		if err := postContainersWait(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
-		apiWait := &ApiWait{}
+		apiWait := &APIWait{}
 		if err := json.Unmarshal(r.Body.Bytes(), apiWait); err != nil {
 		if err := json.Unmarshal(r.Body.Bytes(), apiWait); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
@@ -1119,7 +1119,7 @@ func TestPostContainersAttach(t *testing.T) {
 
 
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image:     GetTestImage(runtime).Id,
+			Image:     GetTestImage(runtime).ID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
@@ -1148,12 +1148,12 @@ func TestPostContainersAttach(t *testing.T) {
 			out:              stdoutPipe,
 			out:              stdoutPipe,
 		}
 		}
 
 
-		req, err := http.NewRequest("POST", "/containers/"+container.Id+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
+		req, err := http.NewRequest("POST", "/containers/"+container.ID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
 		if err != nil {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 
 
-		if err := postContainersAttach(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
+		if err := postContainersAttach(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 	}()
 	}()
@@ -1206,7 +1206,7 @@ func TestDeleteContainers(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"touch", "/test"},
 		Cmd:   []string{"touch", "/test"},
 	})
 	})
 	if err != nil {
 	if err != nil {
@@ -1218,19 +1218,19 @@ func TestDeleteContainers(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	req, err := http.NewRequest("DELETE", "/containers/"+container.Id, nil)
+	req, err := http.NewRequest("DELETE", "/containers/"+container.ID, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := deleteContainers(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
+	if err := deleteContainers(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Code != http.StatusNoContent {
 	if r.Code != http.StatusNoContent {
 		t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
 		t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
 	}
 	}
 
 
-	if c := runtime.Get(container.Id); c != nil {
+	if c := runtime.Get(container.ID); c != nil {
 		t.Fatalf("The container as not been deleted")
 		t.Fatalf("The container as not been deleted")
 	}
 	}
 
 

+ 3 - 0
archive.go

@@ -54,6 +54,9 @@ func Tar(path string, compression Compression) (io.Reader, error) {
 func Untar(archive io.Reader, path string) error {
 func Untar(archive io.Reader, path string) error {
 	cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
 	cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
 	cmd.Stdin = archive
 	cmd.Stdin = archive
+	// Hardcode locale environment for predictable outcome regardless of host configuration.
+	//   (see https://github.com/dotcloud/docker/issues/355)
+	cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
 	output, err := cmd.CombinedOutput()
 	output, err := cmd.CombinedOutput()
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("%s: %s", err, output)
 		return fmt.Errorf("%s: %s", err, output)

+ 4 - 4
auth/auth.go

@@ -16,12 +16,12 @@ import (
 const CONFIGFILE = ".dockercfg"
 const CONFIGFILE = ".dockercfg"
 
 
 // the registry server we want to login against
 // the registry server we want to login against
-const INDEX_SERVER = "https://index.docker.io/v1"
+const INDEXSERVER = "https://index.docker.io/v1"
 
 
-//const INDEX_SERVER = "http://indexstaging-docker.dotcloud.com/"
+//const INDEXSERVER = "http://indexstaging-docker.dotcloud.com/"
 
 
 var (
 var (
-	ErrConfigFileMissing error = errors.New("The Auth config file is missing")
+	ErrConfigFileMissing = errors.New("The Auth config file is missing")
 )
 )
 
 
 type AuthConfig struct {
 type AuthConfig struct {
@@ -44,7 +44,7 @@ func IndexServerAddress() string {
 	if os.Getenv("DOCKER_INDEX_URL") != "" {
 	if os.Getenv("DOCKER_INDEX_URL") != "" {
 		return os.Getenv("DOCKER_INDEX_URL") + "/v1"
 		return os.Getenv("DOCKER_INDEX_URL") + "/v1"
 	}
 	}
-	return INDEX_SERVER
+	return INDEXSERVER
 }
 }
 
 
 // create a base64 encoded auth string to store in config
 // create a base64 encoded auth string to store in config

+ 5 - 5
builder.go

@@ -40,7 +40,7 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
 	}
 	}
 
 
 	// Generate id
 	// Generate id
-	id := GenerateId()
+	id := GenerateID()
 	// Generate default hostname
 	// Generate default hostname
 	// FIXME: the lxc template no longer needs to set a default hostname
 	// FIXME: the lxc template no longer needs to set a default hostname
 	if config.Hostname == "" {
 	if config.Hostname == "" {
@@ -49,17 +49,17 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
 
 
 	container := &Container{
 	container := &Container{
 		// FIXME: we should generate the ID here instead of receiving it as an argument
 		// FIXME: we should generate the ID here instead of receiving it as an argument
-		Id:              id,
+		ID:              id,
 		Created:         time.Now(),
 		Created:         time.Now(),
 		Path:            config.Cmd[0],
 		Path:            config.Cmd[0],
 		Args:            config.Cmd[1:], //FIXME: de-duplicate from config
 		Args:            config.Cmd[1:], //FIXME: de-duplicate from config
 		Config:          config,
 		Config:          config,
-		Image:           img.Id, // Always use the resolved image id
+		Image:           img.ID, // Always use the resolved image id
 		NetworkSettings: &NetworkSettings{},
 		NetworkSettings: &NetworkSettings{},
 		// FIXME: do we need to store this in the container?
 		// FIXME: do we need to store this in the container?
 		SysInitPath: sysInitPath,
 		SysInitPath: sysInitPath,
 	}
 	}
-	container.root = builder.runtime.containerRoot(container.Id)
+	container.root = builder.runtime.containerRoot(container.ID)
 	// Step 1: create the container directory.
 	// Step 1: create the container directory.
 	// This doubles as a barrier to avoid race conditions.
 	// This doubles as a barrier to avoid race conditions.
 	if err := os.Mkdir(container.root, 0700); err != nil {
 	if err := os.Mkdir(container.root, 0700); err != nil {
@@ -110,7 +110,7 @@ func (builder *Builder) Commit(container *Container, repository, tag, comment, a
 	}
 	}
 	// Register the image if needed
 	// Register the image if needed
 	if repository != "" {
 	if repository != "" {
-		if err := builder.repositories.Set(repository, tag, img.Id, true); err != nil {
+		if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil {
 			return img, err
 			return img, err
 		}
 		}
 	}
 	}

+ 20 - 20
builder_client.go

@@ -63,11 +63,11 @@ func (b *builderClient) CmdFrom(name string) error {
 		return err
 		return err
 	}
 	}
 
 
-	img := &ApiId{}
+	img := &APIID{}
 	if err := json.Unmarshal(obj, img); err != nil {
 	if err := json.Unmarshal(obj, img); err != nil {
 		return err
 		return err
 	}
 	}
-	b.image = img.Id
+	b.image = img.ID
 	utils.Debugf("Using image %s", b.image)
 	utils.Debugf("Using image %s", b.image)
 	return nil
 	return nil
 }
 }
@@ -91,19 +91,19 @@ func (b *builderClient) CmdRun(args string) error {
 	b.config.Cmd = nil
 	b.config.Cmd = nil
 	MergeConfig(b.config, config)
 	MergeConfig(b.config, config)
 
 
-	body, statusCode, err := b.cli.call("POST", "/images/getCache", &ApiImageConfig{Id: b.image, Config: b.config})
+	body, statusCode, err := b.cli.call("POST", "/images/getCache", &APIImageConfig{ID: b.image, Config: b.config})
 	if err != nil {
 	if err != nil {
 		if statusCode != 404 {
 		if statusCode != 404 {
 			return err
 			return err
 		}
 		}
 	}
 	}
 	if statusCode != 404 {
 	if statusCode != 404 {
-		apiId := &ApiId{}
-		if err := json.Unmarshal(body, apiId); err != nil {
+		apiID := &APIID{}
+		if err := json.Unmarshal(body, apiID); err != nil {
 			return err
 			return err
 		}
 		}
 		utils.Debugf("Use cached version")
 		utils.Debugf("Use cached version")
-		b.image = apiId.Id
+		b.image = apiID.ID
 		return nil
 		return nil
 	}
 	}
 	cid, err := b.run()
 	cid, err := b.run()
@@ -163,7 +163,7 @@ func (b *builderClient) CmdInsert(args string) error {
 	// 	return err
 	// 	return err
 	// }
 	// }
 
 
-	// apiId := &ApiId{}
+	// apiId := &APIId{}
 	// if err := json.Unmarshal(body, apiId); err != nil {
 	// if err := json.Unmarshal(body, apiId); err != nil {
 	// 	return err
 	// 	return err
 	// }
 	// }
@@ -182,7 +182,7 @@ func (b *builderClient) run() (string, error) {
 		return "", err
 		return "", err
 	}
 	}
 
 
-	apiRun := &ApiRun{}
+	apiRun := &APIRun{}
 	if err := json.Unmarshal(body, apiRun); err != nil {
 	if err := json.Unmarshal(body, apiRun); err != nil {
 		return "", err
 		return "", err
 	}
 	}
@@ -191,18 +191,18 @@ func (b *builderClient) run() (string, error) {
 	}
 	}
 
 
 	//start the container
 	//start the container
-	_, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/start", nil)
+	_, _, err = b.cli.call("POST", "/containers/"+apiRun.ID+"/start", nil)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	b.tmpContainers[apiRun.Id] = struct{}{}
+	b.tmpContainers[apiRun.ID] = struct{}{}
 
 
 	// Wait for it to finish
 	// Wait for it to finish
-	body, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/wait", nil)
+	body, _, err = b.cli.call("POST", "/containers/"+apiRun.ID+"/wait", nil)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	apiWait := &ApiWait{}
+	apiWait := &APIWait{}
 	if err := json.Unmarshal(body, apiWait); err != nil {
 	if err := json.Unmarshal(body, apiWait); err != nil {
 		return "", err
 		return "", err
 	}
 	}
@@ -210,7 +210,7 @@ func (b *builderClient) run() (string, error) {
 		return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, apiWait.StatusCode)
 		return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, apiWait.StatusCode)
 	}
 	}
 
 
-	return apiRun.Id, nil
+	return apiRun.ID, nil
 }
 }
 
 
 func (b *builderClient) commit(id string) error {
 func (b *builderClient) commit(id string) error {
@@ -222,11 +222,11 @@ func (b *builderClient) commit(id string) error {
 	if id == "" {
 	if id == "" {
 		cmd := b.config.Cmd
 		cmd := b.config.Cmd
 		b.config.Cmd = []string{"true"}
 		b.config.Cmd = []string{"true"}
-		if cid, err := b.run(); err != nil {
+		cid, err := b.run()
+		if err != nil {
 			return err
 			return err
-		} else {
-			id = cid
 		}
 		}
+		id = cid
 		b.config.Cmd = cmd
 		b.config.Cmd = cmd
 	}
 	}
 
 
@@ -239,12 +239,12 @@ func (b *builderClient) commit(id string) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	apiId := &ApiId{}
-	if err := json.Unmarshal(body, apiId); err != nil {
+	apiID := &APIID{}
+	if err := json.Unmarshal(body, apiID); err != nil {
 		return err
 		return err
 	}
 	}
-	b.tmpImages[apiId.Id] = struct{}{}
-	b.image = apiId.Id
+	b.tmpImages[apiID.ID] = struct{}{}
+	b.image = apiID.ID
 	b.needCommit = false
 	b.needCommit = false
 	return nil
 	return nil
 }
 }

+ 71 - 84
buildfile.go

@@ -32,8 +32,6 @@ type buildFile struct {
 	tmpContainers map[string]struct{}
 	tmpContainers map[string]struct{}
 	tmpImages     map[string]struct{}
 	tmpImages     map[string]struct{}
 
 
-	needCommit bool
-
 	out io.Writer
 	out io.Writer
 }
 }
 
 
@@ -63,7 +61,7 @@ func (b *buildFile) CmdFrom(name string) error {
 				remote = name
 				remote = name
 			}
 			}
 
 
-			if err := b.srv.ImagePull(remote, tag, "", b.out, false); err != nil {
+			if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false)); err != nil {
 				return err
 				return err
 			}
 			}
 
 
@@ -75,15 +73,14 @@ func (b *buildFile) CmdFrom(name string) error {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	b.image = image.Id
+	b.image = image.ID
 	b.config = &Config{}
 	b.config = &Config{}
 	return nil
 	return nil
 }
 }
 
 
 func (b *buildFile) CmdMaintainer(name string) error {
 func (b *buildFile) CmdMaintainer(name string) error {
-	b.needCommit = true
 	b.maintainer = name
 	b.maintainer = name
-	return nil
+	return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", name))
 }
 }
 
 
 func (b *buildFile) CmdRun(args string) error {
 func (b *buildFile) CmdRun(args string) error {
@@ -95,28 +92,34 @@ func (b *buildFile) CmdRun(args string) error {
 		return err
 		return err
 	}
 	}
 
 
-	cmd, env := b.config.Cmd, b.config.Env
+	cmd := b.config.Cmd
 	b.config.Cmd = nil
 	b.config.Cmd = nil
 	MergeConfig(b.config, config)
 	MergeConfig(b.config, config)
 
 
-	if cache, err := b.srv.ImageGetCached(b.image, config); err != nil {
+	utils.Debugf("Command to be executed: %v", b.config.Cmd)
+
+	if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
 		return err
 		return err
 	} else if cache != nil {
 	} else if cache != nil {
-		utils.Debugf("Use cached version")
-		b.image = cache.Id
+		utils.Debugf("[BUILDER] Use cached version")
+		b.image = cache.ID
 		return nil
 		return nil
+	} else {
+		utils.Debugf("[BUILDER] Cache miss")
 	}
 	}
 
 
 	cid, err := b.run()
 	cid, err := b.run()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	b.config.Cmd, b.config.Env = cmd, env
-	return b.commit(cid)
+	if err := b.commit(cid, cmd, "run"); err != nil {
+		return err
+	}
+	b.config.Cmd = cmd
+	return nil
 }
 }
 
 
 func (b *buildFile) CmdEnv(args string) error {
 func (b *buildFile) CmdEnv(args string) error {
-	b.needCommit = true
 	tmp := strings.SplitN(args, " ", 2)
 	tmp := strings.SplitN(args, " ", 2)
 	if len(tmp) != 2 {
 	if len(tmp) != 2 {
 		return fmt.Errorf("Invalid ENV format")
 		return fmt.Errorf("Invalid ENV format")
@@ -131,60 +134,34 @@ func (b *buildFile) CmdEnv(args string) error {
 		}
 		}
 	}
 	}
 	b.config.Env = append(b.config.Env, key+"="+value)
 	b.config.Env = append(b.config.Env, key+"="+value)
-	return nil
+	return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s=%s", key, value))
 }
 }
 
 
 func (b *buildFile) CmdCmd(args string) error {
 func (b *buildFile) CmdCmd(args string) error {
-	b.needCommit = true
 	var cmd []string
 	var cmd []string
 	if err := json.Unmarshal([]byte(args), &cmd); err != nil {
 	if err := json.Unmarshal([]byte(args), &cmd); err != nil {
 		utils.Debugf("Error unmarshalling: %s, using /bin/sh -c", err)
 		utils.Debugf("Error unmarshalling: %s, using /bin/sh -c", err)
-		b.config.Cmd = []string{"/bin/sh", "-c", args}
-	} else {
-		b.config.Cmd = cmd
+		cmd = []string{"/bin/sh", "-c", args}
+	}
+	if err := b.commit("", cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
+		return err
 	}
 	}
+	b.config.Cmd = cmd
 	return nil
 	return nil
 }
 }
 
 
 func (b *buildFile) CmdExpose(args string) error {
 func (b *buildFile) CmdExpose(args string) error {
 	ports := strings.Split(args, " ")
 	ports := strings.Split(args, " ")
 	b.config.PortSpecs = append(ports, b.config.PortSpecs...)
 	b.config.PortSpecs = append(ports, b.config.PortSpecs...)
-	return nil
+	return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
 }
 }
 
 
 func (b *buildFile) CmdInsert(args string) error {
 func (b *buildFile) CmdInsert(args string) error {
-	if b.image == "" {
-		return fmt.Errorf("Please provide a source image with `from` prior to insert")
-	}
-	tmp := strings.SplitN(args, " ", 2)
-	if len(tmp) != 2 {
-		return fmt.Errorf("Invalid INSERT format")
-	}
-	sourceUrl := strings.Trim(tmp[0], " ")
-	destPath := strings.Trim(tmp[1], " ")
-
-	file, err := utils.Download(sourceUrl, b.out)
-	if err != nil {
-		return err
-	}
-	defer file.Body.Close()
-
-	b.config.Cmd = []string{"echo", "INSERT", sourceUrl, "in", destPath}
-	cid, err := b.run()
-	if err != nil {
-		return err
-	}
-
-	container := b.runtime.Get(cid)
-	if container == nil {
-		return fmt.Errorf("An error occured while creating the container")
-	}
-
-	if err := container.Inject(file.Body, destPath); err != nil {
-		return err
-	}
+	return fmt.Errorf("INSERT has been deprecated. Please use ADD instead")
+}
 
 
-	return b.commit(cid)
+func (b *buildFile) CmdCopy(args string) error {
+	return fmt.Errorf("COPY has been deprecated. Please use ADD instead")
 }
 }
 
 
 func (b *buildFile) CmdAdd(args string) error {
 func (b *buildFile) CmdAdd(args string) error {
@@ -193,12 +170,13 @@ func (b *buildFile) CmdAdd(args string) error {
 	}
 	}
 	tmp := strings.SplitN(args, " ", 2)
 	tmp := strings.SplitN(args, " ", 2)
 	if len(tmp) != 2 {
 	if len(tmp) != 2 {
-		return fmt.Errorf("Invalid INSERT format")
+		return fmt.Errorf("Invalid ADD format")
 	}
 	}
 	orig := strings.Trim(tmp[0], " ")
 	orig := strings.Trim(tmp[0], " ")
 	dest := strings.Trim(tmp[1], " ")
 	dest := strings.Trim(tmp[1], " ")
 
 
-	b.config.Cmd = []string{"echo", "PUSH", orig, "in", dest}
+	cmd := b.config.Cmd
+	b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)}
 	cid, err := b.run()
 	cid, err := b.run()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -208,19 +186,23 @@ func (b *buildFile) CmdAdd(args string) error {
 	if container == nil {
 	if container == nil {
 		return fmt.Errorf("Error while creating the container (CmdAdd)")
 		return fmt.Errorf("Error while creating the container (CmdAdd)")
 	}
 	}
-
-	if err := os.MkdirAll(path.Join(container.rwPath(), dest), 0700); err != nil {
+	if err := container.EnsureMounted(); err != nil {
 		return err
 		return err
 	}
 	}
+	defer container.Unmount()
 
 
 	origPath := path.Join(b.context, orig)
 	origPath := path.Join(b.context, orig)
-	destPath := path.Join(container.rwPath(), dest)
+	destPath := path.Join(container.RootfsPath(), dest)
 
 
 	fi, err := os.Stat(origPath)
 	fi, err := os.Stat(origPath)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	if fi.IsDir() {
 	if fi.IsDir() {
+		if err := os.MkdirAll(destPath, 0700); err != nil {
+			return err
+		}
+
 		files, err := ioutil.ReadDir(path.Join(b.context, orig))
 		files, err := ioutil.ReadDir(path.Join(b.context, orig))
 		if err != nil {
 		if err != nil {
 			return err
 			return err
@@ -231,12 +213,18 @@ func (b *buildFile) CmdAdd(args string) error {
 			}
 			}
 		}
 		}
 	} else {
 	} else {
+		if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil {
+			return err
+		}
 		if err := utils.CopyDirectory(origPath, destPath); err != nil {
 		if err := utils.CopyDirectory(origPath, destPath); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
-
-	return b.commit(cid)
+	if err := b.commit(cid, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil {
+		return err
+	}
+	b.config.Cmd = cmd
+	return nil
 }
 }
 
 
 func (b *buildFile) run() (string, error) {
 func (b *buildFile) run() (string, error) {
@@ -250,7 +238,7 @@ func (b *buildFile) run() (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	b.tmpContainers[c.Id] = struct{}{}
+	b.tmpContainers[c.ID] = struct{}{}
 
 
 	//start the container
 	//start the container
 	if err := c.Start(); err != nil {
 	if err := c.Start(); err != nil {
@@ -262,23 +250,33 @@ func (b *buildFile) run() (string, error) {
 		return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret)
 		return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret)
 	}
 	}
 
 
-	return c.Id, nil
+	return c.ID, nil
 }
 }
 
 
-func (b *buildFile) commit(id string) error {
+// Commit the container <id> with the autorun command <autoCmd>
+func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
 	if b.image == "" {
 	if b.image == "" {
 		return fmt.Errorf("Please provide a source image with `from` prior to commit")
 		return fmt.Errorf("Please provide a source image with `from` prior to commit")
 	}
 	}
 	b.config.Image = b.image
 	b.config.Image = b.image
 	if id == "" {
 	if id == "" {
-		cmd := b.config.Cmd
-		b.config.Cmd = []string{"true"}
-		if cid, err := b.run(); err != nil {
+		b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
+
+		if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
 			return err
 			return err
+		} else if cache != nil {
+			utils.Debugf("[BUILDER] Use cached version")
+			b.image = cache.ID
+			return nil
 		} else {
 		} else {
-			id = cid
+			utils.Debugf("[BUILDER] Cache miss")
+		}
+
+		cid, err := b.run()
+		if err != nil {
+			return err
 		}
 		}
-		b.config.Cmd = cmd
+		id = cid
 	}
 	}
 
 
 	container := b.runtime.Get(id)
 	container := b.runtime.Get(id)
@@ -286,20 +284,20 @@ func (b *buildFile) commit(id string) error {
 		return fmt.Errorf("An error occured while creating the container")
 		return fmt.Errorf("An error occured while creating the container")
 	}
 	}
 
 
+	// Note: Actually copy the struct
+	autoConfig := *b.config
+	autoConfig.Cmd = autoCmd
 	// Commit the container
 	// Commit the container
-	image, err := b.builder.Commit(container, "", "", "", b.maintainer, nil)
+	image, err := b.builder.Commit(container, "", "", "", b.maintainer, &autoConfig)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	b.tmpImages[image.Id] = struct{}{}
-	b.image = image.Id
-	b.needCommit = false
+	b.tmpImages[image.ID] = struct{}{}
+	b.image = image.ID
 	return nil
 	return nil
 }
 }
 
 
 func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
 func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
-	defer b.clearTmp(b.tmpContainers, b.tmpImages)
-
 	if context != nil {
 	if context != nil {
 		name, err := ioutil.TempDir("/tmp", "docker-build")
 		name, err := ioutil.TempDir("/tmp", "docker-build")
 		if err != nil {
 		if err != nil {
@@ -337,6 +335,7 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
 		method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
 		method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
 		if !exists {
 		if !exists {
 			fmt.Fprintf(b.out, "Skipping unknown instruction %s\n", strings.ToUpper(instruction))
 			fmt.Fprintf(b.out, "Skipping unknown instruction %s\n", strings.ToUpper(instruction))
+			continue
 		}
 		}
 		ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
 		ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
 		if ret != nil {
 		if ret != nil {
@@ -345,22 +344,10 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
 
 
 		fmt.Fprintf(b.out, "===> %v\n", b.image)
 		fmt.Fprintf(b.out, "===> %v\n", b.image)
 	}
 	}
-	if b.needCommit {
-		if err := b.commit(""); err != nil {
-			return "", err
-		}
-	}
 	if b.image != "" {
 	if b.image != "" {
-		// The build is successful, keep the temporary containers and images
-		for i := range b.tmpImages {
-			delete(b.tmpImages, i)
-		}
-		fmt.Fprintf(b.out, "Build success.\n Image id:\n%s\n", b.image)
+		fmt.Fprintf(b.out, "Build successful.\n===> %s\n", b.image)
 		return b.image, nil
 		return b.image, nil
 	}
 	}
-	for i := range b.tmpContainers {
-		delete(b.tmpContainers, i)
-	}
 	return "", fmt.Errorf("An error occured during the build\n")
 	return "", fmt.Errorf("An error occured during the build\n")
 }
 }
 
 

+ 3 - 3
buildfile_test.go

@@ -26,7 +26,7 @@ func TestBuild(t *testing.T) {
 
 
 	buildfile := NewBuildFile(srv, &utils.NopWriter{})
 	buildfile := NewBuildFile(srv, &utils.NopWriter{})
 
 
-	imgId, err := buildfile.Build(strings.NewReader(Dockerfile), nil)
+	imgID, err := buildfile.Build(strings.NewReader(Dockerfile), nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -34,7 +34,7 @@ func TestBuild(t *testing.T) {
 	builder := NewBuilder(runtime)
 	builder := NewBuilder(runtime)
 	container, err := builder.Create(
 	container, err := builder.Create(
 		&Config{
 		&Config{
-			Image: imgId,
+			Image: imgID,
 			Cmd:   []string{"cat", "/tmp/passwd"},
 			Cmd:   []string{"cat", "/tmp/passwd"},
 		},
 		},
 	)
 	)
@@ -53,7 +53,7 @@ func TestBuild(t *testing.T) {
 
 
 	container2, err := builder.Create(
 	container2, err := builder.Create(
 		&Config{
 		&Config{
-			Image: imgId,
+			Image: imgID,
 			Cmd:   []string{"ls", "-d", "/var/run/sshd"},
 			Cmd:   []string{"ls", "-d", "/var/run/sshd"},
 		},
 		},
 	)
 	)

+ 1 - 1
changes.go

@@ -65,7 +65,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
 		file := filepath.Base(path)
 		file := filepath.Base(path)
 		// If there is a whiteout, then the file was removed
 		// If there is a whiteout, then the file was removed
 		if strings.HasPrefix(file, ".wh.") {
 		if strings.HasPrefix(file, ".wh.") {
-			originalFile := strings.TrimLeft(file, ".wh.")
+			originalFile := file[len(".wh."):]
 			change.Path = filepath.Join(filepath.Dir(path), originalFile)
 			change.Path = filepath.Join(filepath.Dir(path), originalFile)
 			change.Kind = ChangeDelete
 			change.Kind = ChangeDelete
 		} else {
 		} else {

+ 102 - 99
commands.go

@@ -17,6 +17,7 @@ import (
 	"net/url"
 	"net/url"
 	"os"
 	"os"
 	"os/signal"
 	"os/signal"
+	"path"
 	"path/filepath"
 	"path/filepath"
 	"reflect"
 	"reflect"
 	"strconv"
 	"strconv"
@@ -27,10 +28,10 @@ import (
 	"unicode"
 	"unicode"
 )
 )
 
 
-const VERSION = "0.3.3"
+const VERSION = "0.4.0"
 
 
 var (
 var (
-	GIT_COMMIT string
+	GITCOMMIT string
 )
 )
 
 
 func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
 func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
@@ -101,7 +102,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 		{"stop", "Stop a running container"},
 		{"stop", "Stop a running container"},
 		{"tag", "Tag an image into a repository"},
 		{"tag", "Tag an image into a repository"},
 		{"version", "Show the docker version information"},
 		{"version", "Show the docker version information"},
-		{"wait", "Block until a container stops}, then print its exit code"},
+		{"wait", "Block until a container stops, then print its exit code"},
 	} {
 	} {
 		help += fmt.Sprintf("    %-10.10s%s\n", command[0], command[1])
 		help += fmt.Sprintf("    %-10.10s%s\n", command[0], command[1])
 	}
 	}
@@ -130,16 +131,20 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
 }
 }
 
 
 func (cli *DockerCli) CmdBuild(args ...string) error {
 func (cli *DockerCli) CmdBuild(args ...string) error {
-	cmd := Subcmd("build", "[OPTIONS] [CONTEXT]", "Build an image from a Dockerfile")
-	fileName := cmd.String("f", "Dockerfile", "Use `file` as Dockerfile. Can be '-' for stdin")
+	cmd := Subcmd("build", "[OPTIONS] PATH | -", "Build a new container image from the source code at PATH")
+	tag := cmd.String("t", "", "Tag to be applied to the resulting image in case of success")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
 	}
 	}
+	if cmd.NArg() != 1 {
+		cmd.Usage()
+		return nil
+	}
 
 
 	var (
 	var (
-		file          io.ReadCloser
 		multipartBody io.Reader
 		multipartBody io.Reader
-		err           error
+		file          io.ReadCloser
+		contextPath   string
 	)
 	)
 
 
 	// Init the needed component for the Multipart
 	// Init the needed component for the Multipart
@@ -148,27 +153,19 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	w := multipart.NewWriter(buff)
 	w := multipart.NewWriter(buff)
 	boundary := strings.NewReader("\r\n--" + w.Boundary() + "--\r\n")
 	boundary := strings.NewReader("\r\n--" + w.Boundary() + "--\r\n")
 
 
-	// Create a FormFile multipart for the Dockerfile
-	if *fileName == "-" {
+	compression := Bzip2
+
+	if cmd.Arg(0) == "-" {
 		file = os.Stdin
 		file = os.Stdin
 	} else {
 	} else {
-		file, err = os.Open(*fileName)
+		// Send Dockerfile from arg/Dockerfile (deprecate later)
+		f, err := os.Open(path.Join(cmd.Arg(0), "Dockerfile"))
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		defer file.Close()
-	}
-	if wField, err := w.CreateFormFile("Dockerfile", *fileName); err != nil {
-		return err
-	} else {
-		io.Copy(wField, file)
-	}
-	multipartBody = io.MultiReader(multipartBody, boundary)
-
-	compression := Bzip2
-
-	// Create a FormFile multipart for the context if needed
-	if cmd.Arg(0) != "" {
+		file = f
+		// Send context from arg
+		// Create a FormFile multipart for the context if needed
 		// FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage?
 		// FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage?
 		context, err := Tar(cmd.Arg(0), compression)
 		context, err := Tar(cmd.Arg(0), compression)
 		if err != nil {
 		if err != nil {
@@ -179,23 +176,32 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		if wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension()); err != nil {
+		wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension())
+		if err != nil {
 			return err
 			return err
-		} else {
-			// FIXME: Find a way to have a progressbar for the upload too
-			io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, "Caching Context %v/%v (%v)\r", false))
 		}
 		}
-
+		// FIXME: Find a way to have a progressbar for the upload too
+		sf := utils.NewStreamFormatter(false)
+		io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, sf.FormatProgress("Caching Context", "%v/%v (%v)"), sf))
 		multipartBody = io.MultiReader(multipartBody, boundary)
 		multipartBody = io.MultiReader(multipartBody, boundary)
 	}
 	}
+	// Create a FormFile multipart for the Dockerfile
+	wField, err := w.CreateFormFile("Dockerfile", "Dockerfile")
+	if err != nil {
+		return err
+	}
+	io.Copy(wField, file)
+	multipartBody = io.MultiReader(multipartBody, boundary)
 
 
+	v := &url.Values{}
+	v.Set("t", *tag)
 	// Send the multipart request with correct content-type
 	// Send the multipart request with correct content-type
-	req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, "/build"), multipartBody)
+	req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s?%s", cli.host, cli.port, "/build", v.Encode()), multipartBody)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	req.Header.Set("Content-Type", w.FormDataContentType())
 	req.Header.Set("Content-Type", w.FormDataContentType())
-	if cmd.Arg(0) != "" {
+	if contextPath != "" {
 		req.Header.Set("X-Docker-Context-Compression", compression.Flag())
 		req.Header.Set("X-Docker-Context-Compression", compression.Flag())
 		fmt.Println("Uploading Context...")
 		fmt.Println("Uploading Context...")
 	}
 	}
@@ -270,9 +276,8 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 	oldState, err := term.SetRawTerminal()
 	oldState, err := term.SetRawTerminal()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
-	} else {
-		defer term.RestoreTerminal(oldState)
 	}
 	}
+	defer term.RestoreTerminal(oldState)
 
 
 	cmd := Subcmd("login", "", "Register or Login to the docker registry server")
 	cmd := Subcmd("login", "", "Register or Login to the docker registry server")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
@@ -325,7 +330,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	var out2 ApiAuth
+	var out2 APIAuth
 	err = json.Unmarshal(body, &out2)
 	err = json.Unmarshal(body, &out2)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -352,7 +357,7 @@ func (cli *DockerCli) CmdWait(args ...string) error {
 		if err != nil {
 		if err != nil {
 			fmt.Printf("%s", err)
 			fmt.Printf("%s", err)
 		} else {
 		} else {
-			var out ApiWait
+			var out APIWait
 			err = json.Unmarshal(body, &out)
 			err = json.Unmarshal(body, &out)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
@@ -380,21 +385,20 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	var out ApiVersion
+	var out APIVersion
 	err = json.Unmarshal(body, &out)
 	err = json.Unmarshal(body, &out)
 	if err != nil {
 	if err != nil {
 		utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
 		utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
 		return err
 		return err
 	}
 	}
-	fmt.Println("Version:", out.Version)
-	fmt.Println("Git Commit:", out.GitCommit)
-	if !out.MemoryLimit {
-		fmt.Println("WARNING: No memory limit support")
+	fmt.Println("Client version:", VERSION)
+	fmt.Println("Server version:", out.Version)
+	if out.GitCommit != "" {
+		fmt.Println("Git commit:", out.GitCommit)
 	}
 	}
-	if !out.SwapLimit {
-		fmt.Println("WARNING: No swap limit support")
+	if out.GoVersion != "" {
+		fmt.Println("Go version:", out.GoVersion)
 	}
 	}
-
 	return nil
 	return nil
 }
 }
 
 
@@ -414,15 +418,24 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	var out ApiInfo
-	err = json.Unmarshal(body, &out)
-	if err != nil {
+	var out APIInfo
+	if err := json.Unmarshal(body, &out); err != nil {
 		return err
 		return err
 	}
 	}
-	fmt.Printf("containers: %d\nversion: %s\nimages: %d\nGo version: %s\n", out.Containers, out.Version, out.Images, out.GoVersion)
-	if out.Debug {
-		fmt.Println("debug mode enabled")
-		fmt.Printf("fds: %d\ngoroutines: %d\n", out.NFd, out.NGoroutines)
+
+	fmt.Printf("Containers: %d\n", out.Containers)
+	fmt.Printf("Images: %d\n", out.Images)
+	if out.Debug || os.Getenv("DEBUG") != "" {
+		fmt.Printf("Debug mode (server): %v\n", out.Debug)
+		fmt.Printf("Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
+		fmt.Printf("Fds: %d\n", out.NFd)
+		fmt.Printf("Goroutines: %d\n", out.NGoroutines)
+	}
+	if !out.MemoryLimit {
+		fmt.Println("WARNING: No memory limit support")
+	}
+	if !out.SwapLimit {
+		fmt.Println("WARNING: No swap limit support")
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -590,7 +603,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	var outs []ApiHistory
+	var outs []APIHistory
 	err = json.Unmarshal(body, &outs)
 	err = json.Unmarshal(body, &outs)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -599,7 +612,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
 	fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
 	fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
 
 
 	for _, out := range outs {
 	for _, out := range outs {
-		fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.Id, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy)
+		fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy)
 	}
 	}
 	w.Flush()
 	w.Flush()
 	return nil
 	return nil
@@ -725,12 +738,6 @@ func (cli *DockerCli) CmdPull(args ...string) error {
 		remote = remoteParts[0]
 		remote = remoteParts[0]
 	}
 	}
 
 
-	if strings.Contains(remote, "/") {
-		if _, err := cli.checkIfLogged(true, "pull"); err != nil {
-			return err
-		}
-	}
-
 	v := url.Values{}
 	v := url.Values{}
 	v.Set("fromImage", remote)
 	v.Set("fromImage", remote)
 	v.Set("tag", *tag)
 	v.Set("tag", *tag)
@@ -778,7 +785,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 			return err
 			return err
 		}
 		}
 
 
-		var outs []ApiImages
+		var outs []APIImages
 		err = json.Unmarshal(body, &outs)
 		err = json.Unmarshal(body, &outs)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
@@ -800,9 +807,9 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 			if !*quiet {
 			if !*quiet {
 				fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag)
 				fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag)
 				if *noTrunc {
 				if *noTrunc {
-					fmt.Fprintf(w, "%s\t", out.Id)
+					fmt.Fprintf(w, "%s\t", out.ID)
 				} else {
 				} else {
-					fmt.Fprintf(w, "%s\t", utils.TruncateId(out.Id))
+					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 ago\t", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))))
 				if out.ParentSize > 0 {
 				if out.ParentSize > 0 {
@@ -812,9 +819,9 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 				}
 				}
 			} else {
 			} else {
 				if *noTrunc {
 				if *noTrunc {
-					fmt.Fprintln(w, out.Id)
+					fmt.Fprintln(w, out.ID)
 				} else {
 				} else {
-					fmt.Fprintln(w, utils.TruncateId(out.Id))
+					fmt.Fprintln(w, utils.TruncateID(out.ID))
 				}
 				}
 			}
 			}
 		}
 		}
@@ -861,7 +868,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	var outs []ApiContainers
+	var outs []APIContainers
 	err = json.Unmarshal(body, &outs)
 	err = json.Unmarshal(body, &outs)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -874,9 +881,9 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 	for _, out := range outs {
 	for _, out := range outs {
 		if !*quiet {
 		if !*quiet {
 			if *noTrunc {
 			if *noTrunc {
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.Id, out.Image, out.Command, out.Status, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports)
+				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
 			} else {
 			} else {
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateId(out.Id), out.Image, utils.Trunc(out.Command, 20), out.Status, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports)
+				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
 			}
 			}
 			if out.SizeRootFs > 0 {
 			if out.SizeRootFs > 0 {
 				fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs))
 				fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs))
@@ -885,9 +892,9 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 			}
 			}
 		} else {
 		} else {
 			if *noTrunc {
 			if *noTrunc {
-				fmt.Fprintln(w, out.Id)
+				fmt.Fprintln(w, out.ID)
 			} else {
 			} else {
-				fmt.Fprintln(w, utils.TruncateId(out.Id))
+				fmt.Fprintln(w, utils.TruncateID(out.ID))
 			}
 			}
 		}
 		}
 	}
 	}
@@ -930,13 +937,13 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	apiId := &ApiId{}
-	err = json.Unmarshal(body, apiId)
+	apiID := &APIID{}
+	err = json.Unmarshal(body, apiID)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	fmt.Println(apiId.Id)
+	fmt.Println(apiID.ID)
 	return nil
 	return nil
 }
 }
 
 
@@ -993,12 +1000,10 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
 		return nil
 		return nil
 	}
 	}
 
 
-	v := url.Values{}
-	v.Set("logs", "1")
-	v.Set("stdout", "1")
-	v.Set("stderr", "1")
-
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), false, nil, os.Stdout); err != nil {
+	if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", nil, os.Stdout); err != nil {
+		return err
+	}
+	if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", nil, os.Stderr); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -1075,7 +1080,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	outs := []ApiSearch{}
+	outs := []APISearch{}
 	err = json.Unmarshal(body, &outs)
 	err = json.Unmarshal(body, &outs)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -1207,7 +1212,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	out := &ApiRun{}
+	out := &APIRun{}
 	err = json.Unmarshal(body, out)
 	err = json.Unmarshal(body, out)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -1228,18 +1233,21 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	}
 	}
 
 
 	//start the container
 	//start the container
-	_, _, err = cli.call("POST", "/containers/"+out.Id+"/start", nil)
+	_, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	if !config.AttachStdout && !config.AttachStderr {
+		fmt.Println(out.ID)
+	}
 	if connections > 0 {
 	if connections > 0 {
 		chErrors := make(chan error, connections)
 		chErrors := make(chan error, connections)
-		cli.monitorTtySize(out.Id)
+		cli.monitorTtySize(out.ID)
 
 
 		if splitStderr && config.AttachStderr {
 		if splitStderr && config.AttachStderr {
 			go func() {
 			go func() {
-				chErrors <- cli.hijack("POST", "/containers/"+out.Id+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr)
+				chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr)
 			}()
 			}()
 		}
 		}
 
 
@@ -1257,7 +1265,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 			v.Set("stderr", "1")
 			v.Set("stderr", "1")
 		}
 		}
 		go func() {
 		go func() {
-			chErrors <- cli.hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout)
+			chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout)
 		}()
 		}()
 		for connections > 0 {
 		for connections > 0 {
 			err := <-chErrors
 			err := <-chErrors
@@ -1267,9 +1275,6 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 			connections -= 1
 			connections -= 1
 		}
 		}
 	}
 	}
-	if !config.AttachStdout && !config.AttachStderr {
-		fmt.Println(out.Id)
-	}
 	return nil
 	return nil
 }
 }
 
 
@@ -1317,7 +1322,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
 		params = bytes.NewBuffer(buf)
 		params = bytes.NewBuffer(buf)
 	}
 	}
 
 
-	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), params)
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), params)
 	if err != nil {
 	if err != nil {
 		return nil, -1, err
 		return nil, -1, err
 	}
 	}
@@ -1349,7 +1354,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
 	if (method == "POST" || method == "PUT") && in == nil {
 	if (method == "POST" || method == "PUT") && in == nil {
 		in = bytes.NewReader([]byte{})
 		in = bytes.NewReader([]byte{})
 	}
 	}
-	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), in)
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), in)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -1374,20 +1379,18 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
 	}
 	}
 
 
 	if resp.Header.Get("Content-Type") == "application/json" {
 	if resp.Header.Get("Content-Type") == "application/json" {
-		type Message struct {
-			Status   string `json:"status,omitempty"`
-			Progress string `json:"progress,omitempty"`
-		}
 		dec := json.NewDecoder(resp.Body)
 		dec := json.NewDecoder(resp.Body)
 		for {
 		for {
-			var m Message
+			var m utils.JSONMessage
 			if err := dec.Decode(&m); err == io.EOF {
 			if err := dec.Decode(&m); err == io.EOF {
 				break
 				break
 			} else if err != nil {
 			} else if err != nil {
 				return err
 				return err
 			}
 			}
 			if m.Progress != "" {
 			if m.Progress != "" {
-				fmt.Fprintf(out, "Downloading %s\r", m.Progress)
+				fmt.Fprintf(out, "%s %s\r", m.Status, m.Progress)
+			} else if m.Error != "" {
+				return fmt.Errorf(m.Error)
 			} else {
 			} else {
 				fmt.Fprintf(out, "%s\n", m.Status)
 				fmt.Fprintf(out, "%s\n", m.Status)
 			}
 			}
@@ -1401,7 +1404,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
 }
 }
 
 
 func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.File, out io.Writer) error {
 func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.File, out io.Writer) error {
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", API_VERSION, path), nil)
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -1422,12 +1425,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
 		return err
 		return err
 	})
 	})
 
 
-	if in != nil && setRawTerminal && term.IsTerminal(int(in.Fd())) && os.Getenv("NORAW") == "" {
-		if oldState, err := term.SetRawTerminal(); err != nil {
+	if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" {
+		oldState, err := term.SetRawTerminal()
+		if err != nil {
 			return err
 			return err
-		} else {
-			defer term.RestoreTerminal(oldState)
 		}
 		}
+		defer term.RestoreTerminal(oldState)
 	}
 	}
 	sendStdin := utils.Go(func() error {
 	sendStdin := utils.Go(func() error {
 		_, err := io.Copy(rwc, in)
 		_, err := io.Copy(rwc, in)
@@ -1441,7 +1444,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
 		return err
 		return err
 	}
 	}
 
 
-	if !term.IsTerminal(int(os.Stdin.Fd())) {
+	if !term.IsTerminal(os.Stdin.Fd()) {
 		if err := <-sendStdin; err != nil {
 		if err := <-sendStdin; err != nil {
 			return err
 			return err
 		}
 		}

+ 46 - 45
container.go

@@ -24,7 +24,7 @@ import (
 type Container struct {
 type Container struct {
 	root string
 	root string
 
 
-	Id string
+	ID string
 
 
 	Created time.Time
 	Created time.Time
 
 
@@ -168,8 +168,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet
 }
 }
 
 
 type NetworkSettings struct {
 type NetworkSettings struct {
-	IpAddress   string
-	IpPrefixLen int
+	IPAddress   string
+	IPPrefixLen int
 	Gateway     string
 	Gateway     string
 	Bridge      string
 	Bridge      string
 	PortMapping map[string]string
 	PortMapping map[string]string
@@ -410,7 +410,7 @@ func (container *Container) Start() error {
 	defer container.State.unlock()
 	defer container.State.unlock()
 
 
 	if container.State.Running {
 	if container.State.Running {
-		return fmt.Errorf("The container %s is already running.", container.Id)
+		return fmt.Errorf("The container %s is already running.", container.ID)
 	}
 	}
 	if err := container.EnsureMounted(); err != nil {
 	if err := container.EnsureMounted(); err != nil {
 		return err
 		return err
@@ -432,24 +432,24 @@ func (container *Container) Start() error {
 
 
 	// Create the requested volumes volumes
 	// Create the requested volumes volumes
 	for volPath := range container.Config.Volumes {
 	for volPath := range container.Config.Volumes {
-		if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil {
+		c, err := container.runtime.volumes.Create(nil, container, "", "", nil)
+		if err != nil {
 			return err
 			return err
-		} else {
-			if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
-				return nil
-			}
-			container.Volumes[volPath] = c.Id
 		}
 		}
+		if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
+			return nil
+		}
+		container.Volumes[volPath] = c.ID
 	}
 	}
 
 
 	if container.Config.VolumesFrom != "" {
 	if container.Config.VolumesFrom != "" {
 		c := container.runtime.Get(container.Config.VolumesFrom)
 		c := container.runtime.Get(container.Config.VolumesFrom)
 		if c == nil {
 		if c == nil {
-			return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.Id)
+			return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
 		}
 		}
 		for volPath, id := range c.Volumes {
 		for volPath, id := range c.Volumes {
 			if _, exists := container.Volumes[volPath]; exists {
 			if _, exists := container.Volumes[volPath]; exists {
-				return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.Id)
+				return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.ID)
 			}
 			}
 			if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
 			if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
 				return nil
 				return nil
@@ -463,7 +463,7 @@ func (container *Container) Start() error {
 	}
 	}
 
 
 	params := []string{
 	params := []string{
-		"-n", container.Id,
+		"-n", container.ID,
 		"-f", container.lxcConfigPath(),
 		"-f", container.lxcConfigPath(),
 		"--",
 		"--",
 		"/sbin/init",
 		"/sbin/init",
@@ -574,17 +574,17 @@ func (container *Container) allocateNetwork() error {
 	}
 	}
 	container.NetworkSettings.PortMapping = make(map[string]string)
 	container.NetworkSettings.PortMapping = make(map[string]string)
 	for _, spec := range container.Config.PortSpecs {
 	for _, spec := range container.Config.PortSpecs {
-		if nat, err := iface.AllocatePort(spec); err != nil {
+		nat, err := iface.AllocatePort(spec)
+		if err != nil {
 			iface.Release()
 			iface.Release()
 			return err
 			return err
-		} else {
-			container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
 		}
 		}
+		container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
 	}
 	}
 	container.network = iface
 	container.network = iface
 	container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
 	container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
-	container.NetworkSettings.IpAddress = iface.IPNet.IP.String()
-	container.NetworkSettings.IpPrefixLen, _ = iface.IPNet.Mask.Size()
+	container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
+	container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
 	container.NetworkSettings.Gateway = iface.Gateway.String()
 	container.NetworkSettings.Gateway = iface.Gateway.String()
 	return nil
 	return nil
 }
 }
@@ -598,16 +598,16 @@ func (container *Container) releaseNetwork() {
 // FIXME: replace this with a control socket within docker-init
 // FIXME: replace this with a control socket within docker-init
 func (container *Container) waitLxc() error {
 func (container *Container) waitLxc() error {
 	for {
 	for {
-		if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
+		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
+		if err != nil {
 			return err
 			return err
-		} else {
-			if !strings.Contains(string(output), "RUNNING") {
-				return nil
-			}
+		}
+		if !strings.Contains(string(output), "RUNNING") {
+			return nil
 		}
 		}
 		time.Sleep(500 * time.Millisecond)
 		time.Sleep(500 * time.Millisecond)
 	}
 	}
-	return nil
+	panic("Unreachable")
 }
 }
 
 
 func (container *Container) monitor() {
 func (container *Container) monitor() {
@@ -617,17 +617,17 @@ func (container *Container) monitor() {
 	// If the command does not exists, try to wait via lxc
 	// If the command does not exists, try to wait via lxc
 	if container.cmd == nil {
 	if container.cmd == nil {
 		if err := container.waitLxc(); err != nil {
 		if err := container.waitLxc(); err != nil {
-			utils.Debugf("%s: Process: %s", container.Id, err)
+			utils.Debugf("%s: Process: %s", container.ID, err)
 		}
 		}
 	} else {
 	} else {
 		if err := container.cmd.Wait(); err != nil {
 		if err := container.cmd.Wait(); err != nil {
 			// Discard the error as any signals or non 0 returns will generate an error
 			// Discard the error as any signals or non 0 returns will generate an error
-			utils.Debugf("%s: Process: %s", container.Id, err)
+			utils.Debugf("%s: Process: %s", container.ID, err)
 		}
 		}
 	}
 	}
 	utils.Debugf("Process finished")
 	utils.Debugf("Process finished")
 
 
-	var exitCode int = -1
+	exitCode := -1
 	if container.cmd != nil {
 	if container.cmd != nil {
 		exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
 		exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
 	}
 	}
@@ -636,24 +636,24 @@ func (container *Container) monitor() {
 	container.releaseNetwork()
 	container.releaseNetwork()
 	if container.Config.OpenStdin {
 	if container.Config.OpenStdin {
 		if err := container.stdin.Close(); err != nil {
 		if err := container.stdin.Close(); err != nil {
-			utils.Debugf("%s: Error close stdin: %s", container.Id, err)
+			utils.Debugf("%s: Error close stdin: %s", container.ID, err)
 		}
 		}
 	}
 	}
 	if err := container.stdout.CloseWriters(); err != nil {
 	if err := container.stdout.CloseWriters(); err != nil {
-		utils.Debugf("%s: Error close stdout: %s", container.Id, err)
+		utils.Debugf("%s: Error close stdout: %s", container.ID, err)
 	}
 	}
 	if err := container.stderr.CloseWriters(); err != nil {
 	if err := container.stderr.CloseWriters(); err != nil {
-		utils.Debugf("%s: Error close stderr: %s", container.Id, err)
+		utils.Debugf("%s: Error close stderr: %s", container.ID, err)
 	}
 	}
 
 
 	if container.ptyMaster != nil {
 	if container.ptyMaster != nil {
 		if err := container.ptyMaster.Close(); err != nil {
 		if err := container.ptyMaster.Close(); err != nil {
-			utils.Debugf("%s: Error closing Pty master: %s", container.Id, err)
+			utils.Debugf("%s: Error closing Pty master: %s", container.ID, err)
 		}
 		}
 	}
 	}
 
 
 	if err := container.Unmount(); err != nil {
 	if err := container.Unmount(); err != nil {
-		log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
+		log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
 	}
 	}
 
 
 	// Re-create a brand new stdin pipe once the container exited
 	// Re-create a brand new stdin pipe once the container exited
@@ -674,7 +674,7 @@ func (container *Container) monitor() {
 		// This is because State.setStopped() has already been called, and has caused Wait()
 		// This is because State.setStopped() has already been called, and has caused Wait()
 		// to return.
 		// to return.
 		// FIXME: why are we serializing running state to disk in the first place?
 		// FIXME: why are we serializing running state to disk in the first place?
-		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.Id, err)
+		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
 	}
 	}
 }
 }
 
 
@@ -684,17 +684,17 @@ func (container *Container) kill() error {
 	}
 	}
 
 
 	// Sending SIGKILL to the process via lxc
 	// Sending SIGKILL to the process via lxc
-	output, err := exec.Command("lxc-kill", "-n", container.Id, "9").CombinedOutput()
+	output, err := exec.Command("lxc-kill", "-n", container.ID, "9").CombinedOutput()
 	if err != nil {
 	if err != nil {
-		log.Printf("error killing container %s (%s, %s)", container.Id, output, err)
+		log.Printf("error killing container %s (%s, %s)", container.ID, output, err)
 	}
 	}
 
 
 	// 2. Wait for the process to die, in last resort, try to kill the process directly
 	// 2. Wait for the process to die, in last resort, try to kill the process directly
 	if err := container.WaitTimeout(10 * time.Second); err != nil {
 	if err := container.WaitTimeout(10 * time.Second); err != nil {
 		if container.cmd == nil {
 		if container.cmd == nil {
-			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.Id)
+			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ID)
 		}
 		}
-		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 SIGKILL - trying direct SIGKILL", container.ID)
 		if err := container.cmd.Process.Kill(); err != nil {
 		if err := container.cmd.Process.Kill(); err != nil {
 			return err
 			return err
 		}
 		}
@@ -722,7 +722,7 @@ func (container *Container) Stop(seconds int) error {
 	}
 	}
 
 
 	// 1. Send a SIGTERM
 	// 1. Send a SIGTERM
-	if output, err := exec.Command("lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
+	if output, err := exec.Command("lxc-kill", "-n", container.ID, "15").CombinedOutput(); err != nil {
 		log.Print(string(output))
 		log.Print(string(output))
 		log.Print("Failed to send SIGTERM to the process, force killing")
 		log.Print("Failed to send SIGTERM to the process, force killing")
 		if err := container.kill(); err != nil {
 		if err := container.kill(); err != nil {
@@ -732,7 +732,7 @@ func (container *Container) Stop(seconds int) error {
 
 
 	// 2. Wait for the process to exit on its own
 	// 2. Wait for the process to exit on its own
 	if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
 	if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
-		log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.Id, seconds)
+		log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
 		if err := container.kill(); err != nil {
 		if err := container.kill(); err != nil {
 			return err
 			return err
 		}
 		}
@@ -796,7 +796,8 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
 	case <-done:
 	case <-done:
 		return nil
 		return nil
 	}
 	}
-	panic("unreachable")
+
+	panic("Unreachable")
 }
 }
 
 
 func (container *Container) EnsureMounted() error {
 func (container *Container) EnsureMounted() error {
@@ -839,16 +840,16 @@ func (container *Container) Unmount() error {
 	return Unmount(container.RootfsPath())
 	return Unmount(container.RootfsPath())
 }
 }
 
 
-// ShortId returns a shorthand version of the container's id for convenience.
+// ShortID returns a shorthand version of the container's id for convenience.
 // A collision with other container shorthands is very unlikely, but possible.
 // A collision with other container shorthands is very unlikely, but possible.
 // In case of a collision a lookup with Runtime.Get() will fail, and the caller
 // In case of a collision a lookup with Runtime.Get() will fail, and the caller
 // will need to use a langer prefix, or the full-length container Id.
 // will need to use a langer prefix, or the full-length container Id.
-func (container *Container) ShortId() string {
-	return utils.TruncateId(container.Id)
+func (container *Container) ShortID() string {
+	return utils.TruncateID(container.ID)
 }
 }
 
 
 func (container *Container) logPath(name string) string {
 func (container *Container) logPath(name string) string {
-	return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name))
+	return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
 }
 }
 
 
 func (container *Container) ReadLog(name string) (io.Reader, error) {
 func (container *Container) ReadLog(name string) (io.Reader, error) {
@@ -888,7 +889,7 @@ func (container *Container) rwPath() string {
 	return path.Join(container.root, "rw")
 	return path.Join(container.root, "rw")
 }
 }
 
 
-func validateId(id string) error {
+func validateID(id string) error {
 	if id == "" {
 	if id == "" {
 		return fmt.Errorf("Invalid empty id")
 		return fmt.Errorf("Invalid empty id")
 	}
 	}

+ 64 - 33
container_test.go

@@ -14,7 +14,7 @@ import (
 	"time"
 	"time"
 )
 )
 
 
-func TestIdFormat(t *testing.T) {
+func TestIDFormat(t *testing.T) {
 	runtime, err := newTestRuntime()
 	runtime, err := newTestRuntime()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -22,19 +22,19 @@ func TestIdFormat(t *testing.T) {
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container1, err := NewBuilder(runtime).Create(
 	container1, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
 		},
 		},
 	)
 	)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.Id))
+	match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.ID))
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if !match {
 	if !match {
-		t.Fatalf("Invalid container ID: %s", container1.Id)
+		t.Fatalf("Invalid container ID: %s", container1.ID)
 	}
 	}
 }
 }
 
 
@@ -46,7 +46,7 @@ func TestMultipleAttachRestart(t *testing.T) {
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd: []string{"/bin/sh", "-c",
 			Cmd: []string{"/bin/sh", "-c",
 				"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`;  echo hello; done"},
 				"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`;  echo hello; done"},
 		},
 		},
@@ -153,7 +153,7 @@ func TestDiff(t *testing.T) {
 	// Create a container and remove a file
 	// Create a container and remove a file
 	container1, err := builder.Create(
 	container1, err := builder.Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
 		},
 		},
 	)
 	)
@@ -194,7 +194,7 @@ func TestDiff(t *testing.T) {
 	// Create a new container from the commited image
 	// Create a new container from the commited image
 	container2, err := builder.Create(
 	container2, err := builder.Create(
 		&Config{
 		&Config{
-			Image: img.Id,
+			Image: img.ID,
 			Cmd:   []string{"cat", "/etc/passwd"},
 			Cmd:   []string{"cat", "/etc/passwd"},
 		},
 		},
 	)
 	)
@@ -217,6 +217,37 @@ func TestDiff(t *testing.T) {
 			t.Fatalf("/etc/passwd should not be present in the diff after commit.")
 			t.Fatalf("/etc/passwd should not be present in the diff after commit.")
 		}
 		}
 	}
 	}
+
+	// Create a new containere
+	container3, err := builder.Create(
+		&Config{
+			Image: GetTestImage(runtime).ID,
+			Cmd:   []string{"rm", "/bin/httpd"},
+		},
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer runtime.Destroy(container3)
+
+	if err := container3.Run(); err != nil {
+		t.Fatal(err)
+	}
+
+	// Check the changelog
+	c, err = container3.Changes()
+	if err != nil {
+		t.Fatal(err)
+	}
+	success = false
+	for _, elem := range c {
+		if elem.Path == "/bin/httpd" && elem.Kind == 2 {
+			success = true
+		}
+	}
+	if !success {
+		t.Fatalf("/bin/httpd should be present in the diff after commit.")
+	}
 }
 }
 
 
 func TestCommitAutoRun(t *testing.T) {
 func TestCommitAutoRun(t *testing.T) {
@@ -229,7 +260,7 @@ func TestCommitAutoRun(t *testing.T) {
 	builder := NewBuilder(runtime)
 	builder := NewBuilder(runtime)
 	container1, err := builder.Create(
 	container1, err := builder.Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/sh", "-c", "echo hello > /world"},
 			Cmd:   []string{"/bin/sh", "-c", "echo hello > /world"},
 		},
 		},
 	)
 	)
@@ -260,7 +291,7 @@ func TestCommitAutoRun(t *testing.T) {
 	// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
 	// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
 	container2, err := builder.Create(
 	container2, err := builder.Create(
 		&Config{
 		&Config{
-			Image: img.Id,
+			Image: img.ID,
 		},
 		},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -309,7 +340,7 @@ func TestCommitRun(t *testing.T) {
 
 
 	container1, err := builder.Create(
 	container1, err := builder.Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/sh", "-c", "echo hello > /world"},
 			Cmd:   []string{"/bin/sh", "-c", "echo hello > /world"},
 		},
 		},
 	)
 	)
@@ -341,7 +372,7 @@ func TestCommitRun(t *testing.T) {
 
 
 	container2, err := builder.Create(
 	container2, err := builder.Create(
 		&Config{
 		&Config{
-			Image: img.Id,
+			Image: img.ID,
 			Cmd:   []string{"cat", "/world"},
 			Cmd:   []string{"cat", "/world"},
 		},
 		},
 	)
 	)
@@ -388,7 +419,7 @@ func TestStart(t *testing.T) {
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image:     GetTestImage(runtime).Id,
+			Image:     GetTestImage(runtime).ID,
 			Memory:    33554432,
 			Memory:    33554432,
 			CpuShares: 1000,
 			CpuShares: 1000,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
@@ -432,7 +463,7 @@ func TestRun(t *testing.T) {
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"ls", "-al"},
 			Cmd:   []string{"ls", "-al"},
 		},
 		},
 	)
 	)
@@ -460,7 +491,7 @@ func TestOutput(t *testing.T) {
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(
 	container, err := NewBuilder(runtime).Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foobar"},
 			Cmd:   []string{"echo", "-n", "foobar"},
 		},
 		},
 	)
 	)
@@ -484,7 +515,7 @@ func TestKillDifferentUser(t *testing.T) {
 	}
 	}
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"tail", "-f", "/etc/resolv.conf"},
 		Cmd:   []string{"tail", "-f", "/etc/resolv.conf"},
 		User:  "daemon",
 		User:  "daemon",
 	},
 	},
@@ -532,7 +563,7 @@ func TestKill(t *testing.T) {
 	}
 	}
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat", "/dev/zero"},
 		Cmd:   []string{"cat", "/dev/zero"},
 	},
 	},
 	)
 	)
@@ -580,7 +611,7 @@ func TestExitCode(t *testing.T) {
 	builder := NewBuilder(runtime)
 	builder := NewBuilder(runtime)
 
 
 	trueContainer, err := builder.Create(&Config{
 	trueContainer, err := builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true", ""},
 		Cmd:   []string{"/bin/true", ""},
 	})
 	})
 	if err != nil {
 	if err != nil {
@@ -595,7 +626,7 @@ func TestExitCode(t *testing.T) {
 	}
 	}
 
 
 	falseContainer, err := builder.Create(&Config{
 	falseContainer, err := builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/false", ""},
 		Cmd:   []string{"/bin/false", ""},
 	})
 	})
 	if err != nil {
 	if err != nil {
@@ -617,7 +648,7 @@ func TestRestart(t *testing.T) {
 	}
 	}
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"echo", "-n", "foobar"},
 		Cmd:   []string{"echo", "-n", "foobar"},
 	},
 	},
 	)
 	)
@@ -650,7 +681,7 @@ func TestRestartStdin(t *testing.T) {
 	}
 	}
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 		Cmd:   []string{"cat"},
 
 
 		OpenStdin: true,
 		OpenStdin: true,
@@ -732,7 +763,7 @@ func TestUser(t *testing.T) {
 
 
 	// Default user must be root
 	// Default user must be root
 	container, err := builder.Create(&Config{
 	container, err := builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 	},
 	},
 	)
 	)
@@ -750,7 +781,7 @@ func TestUser(t *testing.T) {
 
 
 	// Set a username
 	// Set a username
 	container, err = builder.Create(&Config{
 	container, err = builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
 		User: "root",
 		User: "root",
@@ -770,7 +801,7 @@ func TestUser(t *testing.T) {
 
 
 	// Set a UID
 	// Set a UID
 	container, err = builder.Create(&Config{
 	container, err = builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
 		User: "0",
 		User: "0",
@@ -790,7 +821,7 @@ func TestUser(t *testing.T) {
 
 
 	// Set a different user by uid
 	// Set a different user by uid
 	container, err = builder.Create(&Config{
 	container, err = builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
 		User: "1",
 		User: "1",
@@ -812,7 +843,7 @@ func TestUser(t *testing.T) {
 
 
 	// Set a different user by username
 	// Set a different user by username
 	container, err = builder.Create(&Config{
 	container, err = builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
 		User: "daemon",
 		User: "daemon",
@@ -841,7 +872,7 @@ func TestMultipleContainers(t *testing.T) {
 	builder := NewBuilder(runtime)
 	builder := NewBuilder(runtime)
 
 
 	container1, err := builder.Create(&Config{
 	container1, err := builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat", "/dev/zero"},
 		Cmd:   []string{"cat", "/dev/zero"},
 	},
 	},
 	)
 	)
@@ -851,7 +882,7 @@ func TestMultipleContainers(t *testing.T) {
 	defer runtime.Destroy(container1)
 	defer runtime.Destroy(container1)
 
 
 	container2, err := builder.Create(&Config{
 	container2, err := builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat", "/dev/zero"},
 		Cmd:   []string{"cat", "/dev/zero"},
 	},
 	},
 	)
 	)
@@ -897,7 +928,7 @@ func TestStdin(t *testing.T) {
 	}
 	}
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 		Cmd:   []string{"cat"},
 
 
 		OpenStdin: true,
 		OpenStdin: true,
@@ -944,7 +975,7 @@ func TestTty(t *testing.T) {
 	}
 	}
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 		Cmd:   []string{"cat"},
 
 
 		OpenStdin: true,
 		OpenStdin: true,
@@ -991,7 +1022,7 @@ func TestEnv(t *testing.T) {
 	}
 	}
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/usr/bin/env"},
 		Cmd:   []string{"/usr/bin/env"},
 	},
 	},
 	)
 	)
@@ -1069,7 +1100,7 @@ func TestLXCConfig(t *testing.T) {
 	cpuMax := 10000
 	cpuMax := 10000
 	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
 	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true"},
 		Cmd:   []string{"/bin/true"},
 
 
 		Hostname:  "foobar",
 		Hostname:  "foobar",
@@ -1097,7 +1128,7 @@ func BenchmarkRunSequencial(b *testing.B) {
 	defer nuke(runtime)
 	defer nuke(runtime)
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {
 		container, err := NewBuilder(runtime).Create(&Config{
 		container, err := NewBuilder(runtime).Create(&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foo"},
 			Cmd:   []string{"echo", "-n", "foo"},
 		},
 		},
 		)
 		)
@@ -1132,7 +1163,7 @@ func BenchmarkRunParallel(b *testing.B) {
 		tasks = append(tasks, complete)
 		tasks = append(tasks, complete)
 		go func(i int, complete chan error) {
 		go func(i int, complete chan error) {
 			container, err := NewBuilder(runtime).Create(&Config{
 			container, err := NewBuilder(runtime).Create(&Config{
-				Image: GetTestImage(runtime).Id,
+				Image: GetTestImage(runtime).ID,
 				Cmd:   []string{"echo", "-n", "foo"},
 				Cmd:   []string{"echo", "-n", "foo"},
 			},
 			},
 			)
 			)

+ 3 - 3
contrib/crashTest.go

@@ -11,13 +11,13 @@ import (
 	"time"
 	"time"
 )
 )
 
 
-var DOCKER_PATH string = path.Join(os.Getenv("DOCKERPATH"), "docker")
+var DOCKERPATH = path.Join(os.Getenv("DOCKERPATH"), "docker")
 
 
 // WARNING: this crashTest will 1) crash your host, 2) remove all containers
 // WARNING: this crashTest will 1) crash your host, 2) remove all containers
 func runDaemon() (*exec.Cmd, error) {
 func runDaemon() (*exec.Cmd, error) {
 	os.Remove("/var/run/docker.pid")
 	os.Remove("/var/run/docker.pid")
 	exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
 	exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
-	cmd := exec.Command(DOCKER_PATH, "-d")
+	cmd := exec.Command(DOCKERPATH, "-d")
 	outPipe, err := cmd.StdoutPipe()
 	outPipe, err := cmd.StdoutPipe()
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -77,7 +77,7 @@ func crashTest() error {
 			stop = false
 			stop = false
 			for i := 0; i < 100 && !stop; {
 			for i := 0; i < 100 && !stop; {
 				func() error {
 				func() error {
-					cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
+					cmd := exec.Command(DOCKERPATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
 					i++
 					i++
 					totalTestCount++
 					totalTestCount++
 					outPipe, err := cmd.StdoutPipe()
 					outPipe, err := cmd.StdoutPipe()

+ 8 - 14
contrib/mkimage-debian.sh

@@ -2,18 +2,15 @@
 set -e
 set -e
 
 
 # these should match the names found at http://www.debian.org/releases/
 # these should match the names found at http://www.debian.org/releases/
-stableSuite='squeeze'
-testingSuite='wheezy'
+stableSuite='wheezy'
+testingSuite='jessie'
 unstableSuite='sid'
 unstableSuite='sid'
 
 
-# if suite is equal to this, it gets the "latest" tag
-latestSuite="$testingSuite"
-
 variant='minbase'
 variant='minbase'
 include='iproute,iputils-ping'
 include='iproute,iputils-ping'
 
 
 repo="$1"
 repo="$1"
-suite="${2:-$latestSuite}"
+suite="${2:-$stableSuite}"
 mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
 mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
 
 
 if [ ! "$repo" ]; then
 if [ ! "$repo" ]; then
@@ -41,17 +38,14 @@ img=$(sudo tar -c . | docker import -)
 # tag suite
 # tag suite
 docker tag $img $repo $suite
 docker tag $img $repo $suite
 
 
-if [ "$suite" = "$latestSuite" ]; then
-	# tag latest
-	docker tag $img $repo latest
-fi
-
 # test the image
 # test the image
 docker run -i -t $repo:$suite echo success
 docker run -i -t $repo:$suite echo success
 
 
-# unstable's version numbers match testing (since it's mostly just a sandbox for testing), so it doesn't get a version number tag
-if [ "$suite" != "$unstableSuite" -a "$suite" != 'unstable' ]; then
-	# tag the specific version
+if [ "$suite" = "$stableSuite" -o "$suite" = 'stable' ]; then
+	# tag latest
+	docker tag $img $repo latest
+	
+	# tag the specific debian release version
 	ver=$(docker run $repo:$suite cat /etc/debian_version)
 	ver=$(docker run $repo:$suite cat /etc/debian_version)
 	docker tag $img $repo $ver
 	docker tag $img $repo $ver
 fi
 fi

+ 2 - 2
docker/docker.go

@@ -15,7 +15,7 @@ import (
 )
 )
 
 
 var (
 var (
-	GIT_COMMIT string
+	GITCOMMIT string
 )
 )
 
 
 func main() {
 func main() {
@@ -59,7 +59,7 @@ func main() {
 	if *flDebug {
 	if *flDebug {
 		os.Setenv("DEBUG", "1")
 		os.Setenv("DEBUG", "1")
 	}
 	}
-	docker.GIT_COMMIT = GIT_COMMIT
+	docker.GITCOMMIT = GITCOMMIT
 	if *flDaemon {
 	if *flDaemon {
 		if flag.NArg() != 0 {
 		if flag.NArg() != 0 {
 			flag.Usage()
 			flag.Usage()

+ 5 - 1
docs/Makefile

@@ -6,6 +6,7 @@ SPHINXOPTS    =
 SPHINXBUILD   = sphinx-build
 SPHINXBUILD   = sphinx-build
 PAPER         =
 PAPER         =
 BUILDDIR      = _build
 BUILDDIR      = _build
+PYTHON        = python
 
 
 # Internal variables.
 # Internal variables.
 PAPEROPT_a4     = -D latex_paper_size=a4
 PAPEROPT_a4     = -D latex_paper_size=a4
@@ -38,6 +39,7 @@ help:
 #	@echo "  linkcheck  to check all external links for integrity"
 #	@echo "  linkcheck  to check all external links for integrity"
 #	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
 #	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
 	@echo "  docs       to build the docs and copy the static files to the outputdir"
 	@echo "  docs       to build the docs and copy the static files to the outputdir"
+	@echo "  server     to serve the docs in your browser under \`http://localhost:8000\`"
 	@echo "  publish    to publish the app to dotcloud"
 	@echo "  publish    to publish the app to dotcloud"
 
 
 clean:
 clean:
@@ -49,6 +51,8 @@ docs:
 	@echo
 	@echo
 	@echo "Build finished. The documentation pages are now in $(BUILDDIR)/html."
 	@echo "Build finished. The documentation pages are now in $(BUILDDIR)/html."
 
 
+server:
+	@cd $(BUILDDIR)/html; $(PYTHON) -m SimpleHTTPServer 8000
 
 
 site:
 site:
 	cp -r website $(BUILDDIR)/
 	cp -r website $(BUILDDIR)/
@@ -59,7 +63,7 @@ site:
 connect:
 connect:
 	@echo connecting dotcloud to www.docker.io website, make sure to use user 1
 	@echo connecting dotcloud to www.docker.io website, make sure to use user 1
 	@cd _build/website/ ; \
 	@cd _build/website/ ; \
-	dotcloud connect dockerwebsite ;
+	dotcloud connect dockerwebsite ; \
 	dotcloud list
 	dotcloud list
 
 
 push:
 push:

+ 11 - 9
docs/README.md

@@ -14,20 +14,22 @@ Installation
 ------------
 ------------
 
 
 * Work in your own fork of the code, we accept pull requests.
 * Work in your own fork of the code, we accept pull requests.
-* Install sphinx: ``pip install sphinx``
-* Install sphinx httpdomain contrib package ``sphinxcontrib-httpdomain``
+* Install sphinx: `pip install sphinx`
+    * Mac OS X: `[sudo] pip-2.7 install sphinx`)
+* Install sphinx httpdomain contrib package: `pip install sphinxcontrib-httpdomain`
+    * Mac OS X: `[sudo] pip-2.7 install sphinxcontrib-httpdomain`
 * If pip is not available you can probably install it using your favorite package manager as **python-pip**
 * If pip is not available you can probably install it using your favorite package manager as **python-pip**
 
 
 Usage
 Usage
 -----
 -----
-* change the .rst files with your favorite editor to your liking
-* run *make docs* to clean up old files and generate new ones
-* your static website can now be found in the _build dir
-* to preview what you have generated, cd into _build/html and then run 'python -m SimpleHTTPServer 8000'
+* Change the `.rst` files with your favorite editor to your liking.
+* Run `make docs` to clean up old files and generate new ones.
+* Your static website can now be found in the `_build` directory.
+* To preview what you have generated run `make server` and open <http://localhost:8000/> in your favorite browser.
 
 
-Working using github's file editor
+Working using GitHub's file editor
 ----------------------------------
 ----------------------------------
-Alternatively, for small changes and typo's you might want to use github's built in file editor. It allows
+Alternatively, for small changes and typo's you might want to use GitHub's built in file editor. It allows
 you to preview your changes right online. Just be carefull not to create many commits.
 you to preview your changes right online. Just be carefull not to create many commits.
 
 
 Images
 Images
@@ -72,4 +74,4 @@ Guides on using sphinx
 
 
 * Code examples
 * Code examples
 
 
-  Start without $, so it's easy to copy and paste.
+  Start without $, so it's easy to copy and paste.

+ 59 - 13
docs/sources/api/docker_remote_api.rst

@@ -15,10 +15,17 @@ Docker Remote API
 - Default port in the docker deamon is 4243 
 - Default port in the docker deamon 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 API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
 
 
-2. Endpoints
+2. Version
+==========
+
+The current verson of the API is 1.1
+Calling /images/<name>/insert is the same as calling /v1.1/images/<name>/insert
+You can still call an old version of the api using /v1.0/images/<name>/insert
+
+3. Endpoints
 ============
 ============
 
 
-2.1 Containers
+3.1 Containers
 --------------
 --------------
 
 
 List containers
 List containers
@@ -132,6 +139,7 @@ Create a container
 	:jsonparam config: the container's configuration
 	:jsonparam config: the container's configuration
 	:statuscode 201: no error
 	:statuscode 201: no error
 	:statuscode 404: no such container
 	:statuscode 404: no such container
+	:statuscode 406: impossible to attach (container not running)
 	:statuscode 500: server error
 	:statuscode 500: server error
 
 
 
 
@@ -459,7 +467,7 @@ Remove a container
         :statuscode 500: server error
         :statuscode 500: server error
 
 
 
 
-2.2 Images
+3.2 Images
 ----------
 ----------
 
 
 List Images
 List Images
@@ -548,7 +556,19 @@ Create an image
 
 
            POST /images/create?fromImage=base HTTP/1.1
            POST /images/create?fromImage=base HTTP/1.1
 
 
-        **Example response**:
+        **Example response v1.1**:
+
+        .. sourcecode:: http
+
+           HTTP/1.1 200 OK
+	   Content-Type: application/json
+
+	   {"status":"Pulling..."}
+	   {"status":"Pulling", "progress":"1/? (n/a)"}
+	   {"error":"Invalid..."}
+	   ...
+
+        **Example response v1.0**:
 
 
         .. sourcecode:: http
         .. sourcecode:: http
 
 
@@ -579,7 +599,19 @@ Insert a file in a image
 
 
            POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
            POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
 
 
-	**Example response**:
+	**Example response v1.1**:
+
+        .. sourcecode:: http
+
+           HTTP/1.1 200 OK
+	   Content-Type: application/json
+
+	   {"status":"Inserting..."}
+	   {"status":"Inserting", "progress":"1/? (n/a)"}
+	   {"error":"Invalid..."}
+	   ...
+
+	**Example response v1.0**:
 
 
         .. sourcecode:: http
         .. sourcecode:: http
 
 
@@ -694,7 +726,19 @@ Push an image on the registry
 
 
 	    POST /images/test/push HTTP/1.1
 	    POST /images/test/push HTTP/1.1
 
 
-	 **Example response**:
+	 **Example response v1.1**:
+
+        .. sourcecode:: http
+
+           HTTP/1.1 200 OK
+	   Content-Type: application/json
+
+	   {"status":"Pushing..."}
+	   {"status":"Pushing", "progress":"1/? (n/a)"}
+	   {"error":"Invalid..."}
+	   ...
+
+	 **Example response v1.0**:
 
 
         .. sourcecode:: http
         .. sourcecode:: http
 
 
@@ -800,7 +844,7 @@ Search images
 	   :statuscode 500: server error
 	   :statuscode 500: server error
 
 
 
 
-2.3 Misc
+3.3 Misc
 --------
 --------
 
 
 Build an image from Dockerfile via stdin
 Build an image from Dockerfile via stdin
@@ -826,6 +870,7 @@ Build an image from Dockerfile via stdin
 	   
 	   
 	   {{ STREAM }}
 	   {{ STREAM }}
 
 
+	:query t: tag to be applied to the resulting image in case of success
 	:statuscode 200: no error
 	:statuscode 200: no error
         :statuscode 500: server error
         :statuscode 500: server error
 
 
@@ -912,10 +957,12 @@ Display system-wide information
 
 
 	   {
 	   {
 		"Containers":11,
 		"Containers":11,
-		"Version":"0.2.2",
 		"Images":16,
 		"Images":16,
-		"GoVersion":"go1.0.3",
-		"Debug":false
+		"Debug":false,
+		"NFd": 11,
+		"NGoroutines":21,
+		"MemoryLimit":true,
+		"SwapLimit":false
 	   }
 	   }
 
 
         :statuscode 200: no error
         :statuscode 200: no error
@@ -941,12 +988,11 @@ Show the docker version information
 
 
            HTTP/1.1 200 OK
            HTTP/1.1 200 OK
 	   Content-Type: application/json
 	   Content-Type: application/json
-	   
+
 	   {
 	   {
 		"Version":"0.2.2",
 		"Version":"0.2.2",
 		"GitCommit":"5a2a5cc+CHANGES",
 		"GitCommit":"5a2a5cc+CHANGES",
-		"MemoryLimit":true,
-		"SwapLimit":false
+		"GoVersion":"go1.0.3"
 	   }
 	   }
 
 
         :statuscode 200: no error
         :statuscode 200: no error

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

@@ -2,8 +2,8 @@
 :description: docker documentation
 :description: docker documentation
 :keywords: docker, ipa, documentation
 :keywords: docker, ipa, documentation
 
 
-API's
-=============
+APIs
+====
 
 
 This following :
 This following :
 
 

+ 0 - 1
docs/sources/api/registry_api.rst

@@ -246,7 +246,6 @@ The Index has two main purposes (along with its fancy social features):
 
 
 - Resolve short names (to avoid passing absolute URLs all the time)
 - Resolve short names (to avoid passing absolute URLs all the time)
    - username/projectname -> \https://registry.docker.io/users/<username>/repositories/<projectname>/
    - username/projectname -> \https://registry.docker.io/users/<username>/repositories/<projectname>/
-   - team/projectname -> \https://registry.docker.io/team/<team>/repositories/<projectname>/
 - Authenticate a user as a repos owner (for a central referenced repository)
 - Authenticate a user as a repos owner (for a central referenced repository)
 
 
 3.1 Without an Index
 3.1 Without an Index

+ 21 - 6
docs/sources/commandline/command/build.rst

@@ -2,12 +2,27 @@
 :description: Build a new image from the Dockerfile passed via stdin
 :description: Build a new image from the Dockerfile passed via stdin
 :keywords: build, docker, container, documentation
 :keywords: build, docker, container, documentation
 
 
-========================================================
-``build`` -- Build a container from Dockerfile via stdin
-========================================================
+================================================
+``build`` -- Build a container from a Dockerfile
+================================================
 
 
 ::
 ::
 
 
-    Usage: docker build -
-    Example: cat Dockerfile | docker build -
-    Build a new image from the Dockerfile passed via stdin
+    Usage: docker build [OPTIONS] PATH | -
+    Build a new container image from the source code at PATH
+      -t="": Tag to be applied to the resulting image in case of success.
+
+Examples
+--------
+
+.. code-block:: bash
+
+    docker build .
+
+This will take the local Dockerfile
+
+.. code-block:: bash
+
+    docker build -
+
+This will read a Dockerfile form Stdin without context

+ 2 - 2
docs/sources/concepts/introduction.rst

@@ -5,8 +5,8 @@
 Introduction
 Introduction
 ============
 ============
 
 
-Docker - The Linux container runtime
-------------------------------------
+Docker -- The Linux container runtime
+-------------------------------------
 
 
 Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
 Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
 
 

+ 2 - 2
docs/sources/contributing/devenvironment.rst

@@ -1,8 +1,8 @@
-:title: Setting up a dev environment
+:title: Setting Up a Dev Environment
 :description: Guides on how to contribute to docker
 :description: Guides on how to contribute to docker
 :keywords: Docker, documentation, developers, contributing, dev environment
 :keywords: Docker, documentation, developers, contributing, dev environment
 
 
-Setting up a dev environment
+Setting Up a Dev Environment
 ============================
 ============================
 
 
 Instructions that have been verified to work on Ubuntu 12.10,
 Instructions that have been verified to work on Ubuntu 12.10,

+ 2 - 2
docs/sources/examples/couchdb_data_volumes.rst

@@ -4,8 +4,8 @@
 
 
 .. _running_couchdb_service:
 .. _running_couchdb_service:
 
 
-Create a CouchDB service
-========================
+CouchDB Service
+===============
 
 
 .. include:: example_header.inc
 .. include:: example_header.inc
 
 

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

@@ -1,6 +1,6 @@
 :title: Docker Examples
 :title: Docker Examples
 :description: Examples on how to use Docker
 :description: Examples on how to use Docker
-:keywords: docker, hello world, examples
+:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples
 
 
 
 
 
 
@@ -16,6 +16,7 @@ Contents:
    hello_world
    hello_world
    hello_world_daemon
    hello_world_daemon
    python_web_app
    python_web_app
+   nodejs_web_app
    running_redis_service
    running_redis_service
    running_ssh_service
    running_ssh_service
    couchdb_data_volumes
    couchdb_data_volumes

+ 236 - 0
docs/sources/examples/nodejs_web_app.rst

@@ -0,0 +1,236 @@
+:title: Running a Node.js app on CentOS
+:description: Installing and running a Node.js app on CentOS
+:keywords: docker, example, package installation, node, centos
+
+.. _nodejs_web_app:
+
+Node.js Web App
+===============
+
+.. include:: example_header.inc
+
+The goal of this example is to show you how you can build your own docker images
+from a parent image using a ``Dockerfile`` . We will do that by making a simple
+Node.js hello world web application running on CentOS. You can get the full
+source code at https://github.com/gasi/docker-node-hello.
+
+Create Node.js app
+++++++++++++++++++
+
+First, create a ``package.json`` file that describes your app and its
+dependencies:
+
+.. code-block:: json
+
+    {
+      "name": "docker-centos-hello",
+      "private": true,
+      "version": "0.0.1",
+      "description": "Node.js Hello World app on CentOS using docker",
+      "author": "Daniel Gasienica <daniel@gasienica.ch>",
+      "dependencies": {
+        "express": "3.2.4"
+      }
+    }
+
+Then, create an ``index.js`` file that defines a web app using the
+`Express.js <http://expressjs.com/>`_ framework:
+
+.. code-block:: javascript
+
+    var express = require('express');
+
+    // Constants
+    var PORT = 8080;
+
+    // App
+    var app = express();
+    app.get('/', function (req, res) {
+      res.send('Hello World\n');
+    });
+
+    app.listen(PORT)
+    console.log('Running on http://localhost:' + PORT);
+
+
+In the next steps, we’ll look at how you can run this app inside a CentOS
+container using docker. First, you’ll need to build a docker image of your app.
+
+Creating a ``Dockerfile``
++++++++++++++++++++++++++
+
+Create an empty file called ``Dockerfile``:
+
+.. code-block:: bash
+
+    touch Dockerfile
+
+Open the ``Dockerfile`` in your favorite text editor and add the following line
+that defines the version of docker the image requires to build
+(this example uses docker 0.3.4):
+
+.. code-block:: bash
+
+    # DOCKER-VERSION 0.3.4
+
+Next, define the parent image you want to use to build your own image on top of.
+Here, we’ll use `CentOS <https://index.docker.io/_/centos/>`_ (tag: ``6.4``)
+available on the `docker index`_:
+
+.. code-block:: bash
+
+    FROM    centos:6.4
+
+Since we’re building a Node.js app, you’ll have to install Node.js as well as
+npm on your CentOS image. Node.js is required to run your app and npm to install
+your app’s dependencies defined in ``package.json``.
+To install the right package for CentOS, we’ll use the instructions from the
+`Node.js wiki`_:
+
+.. code-block:: bash
+
+    # Enable EPEL for Node.js
+    RUN     rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
+    # Install Node.js and npm
+    RUN     yum install -y npm-1.2.17-5.el6
+
+To bundle your app’s source code inside the docker image, use the ``ADD``
+command:
+
+.. code-block:: bash
+
+    # Bundle app source
+    ADD . /src
+
+Install your app dependencies using npm:
+
+.. code-block:: bash
+
+    # Install app dependencies
+    RUN cd /src; npm install
+
+Your app binds to port ``8080`` so you’ll use the ``EXPOSE`` command to have it
+mapped by the docker daemon:
+
+.. code-block:: bash
+
+    EXPOSE  8080
+
+Last but not least, define the command to run your app using ``CMD`` which
+defines your runtime, i.e. ``node``, and the path to our app, i.e.
+``src/index.js`` (see the step where we added the source to the container):
+
+.. code-block:: bash
+
+    CMD ["node", "/src/index.js"]
+
+Your ``Dockerfile`` should now look like this:
+
+.. code-block:: bash
+
+
+    # DOCKER-VERSION 0.3.4
+    FROM    centos:6.4
+
+    # Enable EPEL for Node.js
+    RUN     rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
+    # Install Node.js and npm
+    RUN     yum install -y npm-1.2.17-5.el6
+
+    # Bundle app source
+    ADD . /src
+    # Install app dependencies
+    RUN cd /src; npm install
+
+    EXPOSE  8080
+    CMD ["node", "/src/index.js"]
+
+
+Building your image
++++++++++++++++++++
+
+Go to the directory that has your ``Dockerfile`` and run the following command
+to build a docker image. The ``-t`` flag let’s you tag your image so it’s easier
+to find later using the ``docker images`` command:
+
+.. code-block:: bash
+
+    docker build -t <your username>/centos-node-hello .
+
+Your image will now be listed by docker:
+
+.. code-block:: bash
+
+    docker images
+
+    > # Example
+    > REPOSITORY                 TAG       ID              CREATED
+    > centos                     6.4       539c0211cd76    8 weeks ago
+    > gasi/centos-node-hello     latest    d64d3505b0d2    2 hours ago
+
+
+Run the image
++++++++++++++
+
+Running your image with ``-d`` runs the container in detached mode, leaving the
+container running in the background. Run the image you previously built:
+
+.. code-block:: bash
+
+    docker run -d <your username>/centos-node-hello
+
+Print the output of your app:
+
+.. code-block:: bash
+
+    # Get container ID
+    docker ps
+
+    # Print app output
+    docker logs <container id>
+
+    > # Example
+    > Running on http://localhost:8080
+
+
+Test
+++++
+
+To test your app, get the the port of your app that docker mapped:
+
+.. code-block:: bash
+
+    docker ps
+
+    > # Example
+    > ID            IMAGE                          COMMAND              ...   PORTS
+    > ecce33b30ebf  gasi/centos-node-hello:latest  node /src/index.js         49160->8080
+
+In the example above, docker mapped the ``8080`` port of the container to
+``49160``.
+
+Now you can call your app using ``curl`` (install if needed via:
+``sudo apt-get install curl``):
+
+.. code-block:: bash
+
+    curl -i localhost:49160
+
+    > HTTP/1.1 200 OK
+    > X-Powered-By: Express
+    > Content-Type: text/html; charset=utf-8
+    > Content-Length: 12
+    > Date: Sun, 02 Jun 2013 03:53:22 GMT
+    > Connection: keep-alive
+    >
+    > Hello World
+
+We hope this tutorial helped you get up and running with Node.js and CentOS on
+docker. You can get the full source code at
+https://github.com/gasi/docker-node-hello.
+
+Continue to :ref:`running_redis_service`.
+
+
+.. _Node.js wiki: https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#rhelcentosscientific-linux-6
+.. _docker index: https://index.docker.io/

+ 2 - 2
docs/sources/examples/python_web_app.rst

@@ -4,8 +4,8 @@
 
 
 .. _python_web_app:
 .. _python_web_app:
 
 
-Building a python web app
-=========================
+Python Web App
+==============
 
 
 .. include:: example_header.inc
 .. include:: example_header.inc
 
 

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

@@ -4,7 +4,7 @@
 
 
 .. _running_examples:
 .. _running_examples:
 
 
-Running The Examples
+Running the Examples
 --------------------
 --------------------
 
 
 All the examples assume your machine is running the docker daemon. To run the docker daemon in the background, simply type:
 All the examples assume your machine is running the docker daemon. To run the docker daemon in the background, simply type:

+ 3 - 3
docs/sources/examples/running_redis_service.rst

@@ -4,8 +4,8 @@
 
 
 .. _running_redis_service:
 .. _running_redis_service:
 
 
-Create a redis service
-======================
+Redis Service
+=============
 
 
 .. include:: example_header.inc
 .. include:: example_header.inc
 
 
@@ -34,7 +34,7 @@ Snapshot the installation
 
 
 .. code-block:: bash
 .. code-block:: bash
 
 
-    docker ps -a  # grab the container id (this will be the last one in the list)
+    docker ps -a  # grab the container id (this will be the first one in the list)
     docker commit <container_id> <your username>/redis
     docker commit <container_id> <your username>/redis
 
 
 Run the service
 Run the service

+ 49 - 4
docs/sources/examples/running_ssh_service.rst

@@ -4,8 +4,8 @@
 
 
 .. _running_ssh_service:
 .. _running_ssh_service:
 
 
-Create an ssh daemon service
-============================
+SSH Daemon Service
+==================
 
 
 .. include:: example_header.inc
 .. include:: example_header.inc
 
 
@@ -20,8 +20,7 @@ minutes and not entirely smooth, but gives you a good idea.
     <div style="margin-top:10px;">
     <div style="margin-top:10px;">
       <iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe>
       <iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe>
     </div>
     </div>
-
-
+	
 You can also get this sshd container by using
 You can also get this sshd container by using
 ::
 ::
 
 
@@ -30,3 +29,49 @@ You can also get this sshd container by using
 
 
 The password is 'screencast'
 The password is 'screencast'
 
 
+**Video's Transcription:**
+
+.. code-block:: bash
+
+	 # Hello! We are going to try and install openssh on a container and run it as a servic 
+	 # let's pull base to get a base ubuntu image. 
+	 $ docker pull base
+	 # I had it so it was quick
+	 # now let's connect using -i for interactive and with -t for terminal 
+	 # we execute /bin/bash to get a prompt.
+	 $ docker run -i -t base /bin/bash
+	 # now let's commit it 
+	 # which container was it?
+	 $ docker ps -a |more
+	 $ docker commit a30a3a2f2b130749995f5902f079dc6ad31ea0621fac595128ec59c6da07feea dhrp/sshd 
+	 # I gave the name dhrp/sshd for the container
+	 # now we can run it again 
+	 $ docker run -d dhrp/sshd /usr/sbin/sshd -D # D for daemon mode 
+	 # is it running?
+	 $ docker ps
+	 # yes!
+	 # let's stop it 
+	 $ docker stop 0ebf7cec294755399d063f4b1627980d4cbff7d999f0bc82b59c300f8536a562
+	 $ docker ps
+	 # and reconnect, but now open a port to it
+	 $ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D 
+	 $ docker port b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 22
+	 # it has now given us a port to connect to
+	 # we have to connect using a public ip of our host
+	 $ hostname
+	 $ ifconfig
+	 $ ssh root@192.168.33.10 -p 49153
+	 # Ah! forgot to set root passwd
+	 $ docker commit b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 dhrp/sshd 
+	 $ docker ps -a
+	 $ docker run -i -t dhrp/sshd /bin/bash
+	 $ passwd
+	 $ exit
+	 $ docker commit 9e863f0ca0af31c8b951048ba87641d67c382d08d655c2e4879c51410e0fedc1 dhrp/sshd
+	 $ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D
+	 $ docker port a0aaa9558c90cf5c7782648df904a82365ebacce523e4acc085ac1213bfe2206 22
+	 $ ifconfig
+	 $ ssh root@192.168.33.10 -p 49154
+	 # Thanks for watching, Thatcher thatcher@dotcloud.com
+
+

+ 3 - 4
docs/sources/faq.rst

@@ -19,7 +19,8 @@ Most frequently asked questions.
 
 
 3. **Does Docker run on Mac OS X or Windows?**
 3. **Does Docker run on Mac OS X or Windows?**
 
 
-   Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a virtual machine on your box, and get the best of both worlds. Check out the MacOSX_ and Windows_ installation guides.
+   Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a
+   virtual machine on your box, and get the best of both worlds. Check out the :ref:`install_using_vagrant` and :ref:`windows` installation guides.
 
 
 4. **How do containers compare to virtual machines?**
 4. **How do containers compare to virtual machines?**
 
 
@@ -39,10 +40,8 @@ Most frequently asked questions.
     * `Ask questions on Stackoverflow`_
     * `Ask questions on Stackoverflow`_
     * `Join the conversation on Twitter`_
     * `Join the conversation on Twitter`_
 
 
-    .. _Windows: ../installation/windows/
-    .. _MacOSX: ../installation/vagrant/
     .. _the repo: http://www.github.com/dotcloud/docker
     .. _the repo: http://www.github.com/dotcloud/docker
-    .. _IRC\: docker on freenode: irc://chat.freenode.net#docker
+    .. _IRC: docker on freenode: docker on freenode: irc://chat.freenode.net#docker
     .. _Github: http://www.github.com/dotcloud/docker
     .. _Github: http://www.github.com/dotcloud/docker
     .. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker
     .. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker
     .. _Join the conversation on Twitter: http://twitter.com/getdocker
     .. _Join the conversation on Twitter: http://twitter.com/getdocker

+ 2 - 2
docs/sources/index.rst

@@ -7,8 +7,8 @@
 Introduction
 Introduction
 ============
 ============
 
 
-Docker - The Linux container runtime
-------------------------------------
+Docker -- The Linux container runtime
+-------------------------------------
 
 
 Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
 Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
 
 

+ 10 - 0
docs/sources/installation/ubuntulinux.rst

@@ -92,6 +92,16 @@ have AUFS filesystem support enabled, so we need to install it.
    sudo apt-get update
    sudo apt-get update
    sudo apt-get install linux-image-extra-`uname -r`
    sudo apt-get install linux-image-extra-`uname -r`
 
 
+**add-apt-repository support**
+
+Some installations of Ubuntu 13.04 require ``software-properties-common`` to be
+installed before being able to use add-apt-repository.
+
+.. code-block:: bash
+
+  sudo apt-get install software-properties-common
+
+
 Installation
 Installation
 ------------
 ------------
 
 

+ 1 - 0
docs/sources/installation/windows.rst

@@ -2,6 +2,7 @@
 :description: Docker's tutorial to run docker on Windows
 :description: Docker's tutorial to run docker on Windows
 :keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
 :keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
 
 
+.. _windows:
 
 
 Using Vagrant (Windows)
 Using Vagrant (Windows)
 =======================
 =======================

+ 2 - 2
docs/sources/use/basics.rst

@@ -3,8 +3,8 @@
 :keywords: Examples, Usage, basic commands, docker, documentation, examples
 :keywords: Examples, Usage, basic commands, docker, documentation, examples
 
 
 
 
-The basics
-=============
+The Basics
+==========
 
 
 Starting Docker
 Starting Docker
 ---------------
 ---------------

+ 8 - 2
docs/sources/use/builder.rst

@@ -125,8 +125,14 @@ curl was installed within the image.
 .. note::
 .. note::
     The path must include the file name.
     The path must include the file name.
 
 
-.. note::
-    This instruction has temporarily disabled
+2.8 ADD
+-------
+
+    ``ADD <src> <dest>``
+
+The `ADD` instruction will insert the files from the `<src>` path of the context into `<dest>` path 
+of the container.
+The context must be set in order to use this instruction. (see examples)
 
 
 3. Dockerfile Examples
 3. Dockerfile Examples
 ======================
 ======================

+ 4 - 4
docs/sources/use/workingwithrepository.rst

@@ -4,8 +4,8 @@
 
 
 .. _working_with_the_repository:
 .. _working_with_the_repository:
 
 
-Working with the repository
-============================
+Working with the Repository
+===========================
 
 
 
 
 Top-level repositories and user repositories
 Top-level repositories and user repositories
@@ -14,9 +14,9 @@ Top-level repositories and user repositories
 Generally, there are two types of repositories: Top-level repositories which are controlled by the people behind
 Generally, there are two types of repositories: Top-level repositories which are controlled by the people behind
 Docker, and user repositories.
 Docker, and user repositories.
 
 
-* Top-level repositories can easily be recognized by not having a / (slash) in their name. These repositories can
+* Top-level repositories can easily be recognized by not having a ``/`` (slash) in their name. These repositories can
   generally be trusted.
   generally be trusted.
-* User repositories always come in the form of <username>/<repo_name>. This is what your published images will look like.
+* User repositories always come in the form of ``<username>/<repo_name>``. This is what your published images will look like.
 * User images are not checked, it is therefore up to you whether or not you trust the creator of this image.
 * User images are not checked, it is therefore up to you whether or not you trust the creator of this image.
 
 
 
 

+ 8 - 8
docs/website/gettingstarted/index.html

@@ -62,7 +62,7 @@
 </div>
 </div>
 
 
 <div class="container">
 <div class="container">
-    <div class="alert alert-info">
+    <div class="alert alert-info" style="margin-bottom: 0;">
         <strong>Docker is still under heavy development.</strong> It should not yet be used in production. Check <a href="http://github.com/dotcloud/docker">the repo</a> for recent progress.
         <strong>Docker is still under heavy development.</strong> It should not yet be used in production. Check <a href="http://github.com/dotcloud/docker">the repo</a> for recent progress.
     </div>
     </div>
     <div class="row">
     <div class="row">
@@ -133,13 +133,13 @@
             </section>
             </section>
 
 
             <section class="contentblock">
             <section class="contentblock">
-                <h2>More resources</h2>
-                <ul>
-                    <li><a href="irc://chat.freenode.net#docker">IRC: docker on freenode</a></li>
-                    <li><a href="http://www.github.com/dotcloud/docker">Github</a></li>
-                    <li><a href="http://stackoverflow.com/tags/docker/">Ask questions on Stackoverflow</a></li>
-                    <li><a href="http://twitter.com/getdocker/">Join the conversation on Twitter</a></li>
-                </ul>
+                <h2>Questions? Want to get in touch?</h2>
+                <p>There are several ways to get in touch:</p>
+                <p><strong>Join the discussion on IRC.</strong> We can be found in the <a href="irc://chat.freenode.net#docker">#docker</a> channel on chat.freenode.net</p>
+                <p><strong>Discussions</strong> happen on our google group: <a href="https://groups.google.com/d/forum/docker-club">docker-club at googlegroups.com</a></p>
+                <p>All our <strong>development and decisions</strong> are made out in the open on Github <a href="http://www.github.com/dotcloud/docker">github.com/dotcloud/docker</a></p>
+                <p><strong>Get help on using Docker</strong> by asking on <a href="http://stackoverflow.com/tags/docker/">Stackoverflow</a></p>
+                <p>And of course, <strong>tweet</strong> your tweets to <a href="http://twitter.com/getdocker/">twitter.com/getdocker</a></p>
             </section>
             </section>
 
 
 
 

+ 1 - 1
docs/website/index.html

@@ -270,7 +270,7 @@
                     <li>Filesystem isolation: each process container runs in a completely separate root filesystem.</li>
                     <li>Filesystem isolation: each process container runs in a completely separate root filesystem.</li>
                     <li>Resource isolation: system resources like cpu and memory can be allocated differently to each process container, using cgroups.</li>
                     <li>Resource isolation: system resources like cpu and memory can be allocated differently to each process container, using cgroups.</li>
                     <li>Network isolation: each process container runs in its own network namespace, with a virtual interface and IP address of its own.</li>
                     <li>Network isolation: each process container runs in its own network namespace, with a virtual interface and IP address of its own.</li>
-                    <li>Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremeley fast, memory-cheap and disk-cheap.</li>
+                    <li>Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremely fast, memory-cheap and disk-cheap.</li>
                     <li>Logging: the standard streams (stdout/stderr/stdin) of each process container is collected and logged for real-time or batch retrieval.</li>
                     <li>Logging: the standard streams (stdout/stderr/stdin) of each process container is collected and logged for real-time or batch retrieval.</li>
                     <li>Change management: changes to a container's filesystem can be committed into a new image and re-used to create more containers. No templating or manual configuration required.</li>
                     <li>Change management: changes to a container's filesystem can be committed into a new image and re-used to create more containers. No templating or manual configuration required.</li>
                     <li>Interactive shell: docker can allocate a pseudo-tty and attach to the standard input of any container, for example to run a throwaway interactive shell.</li>
                     <li>Interactive shell: docker can allocate a pseudo-tty and attach to the standard input of any container, for example to run a throwaway interactive shell.</li>

+ 24 - 22
graph.go

@@ -86,8 +86,8 @@ func (graph *Graph) Get(name string) (*Image, error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if img.Id != id {
-		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.Id)
+	if img.ID != id {
+		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
 	}
 	}
 	img.graph = graph
 	img.graph = graph
 	if img.Size == 0 {
 	if img.Size == 0 {
@@ -101,8 +101,8 @@ func (graph *Graph) Get(name string) (*Image, error) {
 	}
 	}
 	graph.lockSumMap.Lock()
 	graph.lockSumMap.Lock()
 	defer graph.lockSumMap.Unlock()
 	defer graph.lockSumMap.Unlock()
-	if _, exists := graph.checksumLock[img.Id]; !exists {
-		graph.checksumLock[img.Id] = &sync.Mutex{}
+	if _, exists := graph.checksumLock[img.ID]; !exists {
+		graph.checksumLock[img.ID] = &sync.Mutex{}
 	}
 	}
 	return img, nil
 	return img, nil
 }
 }
@@ -110,16 +110,17 @@ func (graph *Graph) Get(name string) (*Image, error) {
 // Create creates a new image and registers it in the graph.
 // Create creates a new image and registers it in the graph.
 func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
 func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
 	img := &Image{
 	img := &Image{
-		Id:            GenerateId(),
+		ID:            GenerateID(),
 		Comment:       comment,
 		Comment:       comment,
 		Created:       time.Now(),
 		Created:       time.Now(),
 		DockerVersion: VERSION,
 		DockerVersion: VERSION,
 		Author:        author,
 		Author:        author,
 		Config:        config,
 		Config:        config,
+		Architecture:  "x86_64",
 	}
 	}
 	if container != nil {
 	if container != nil {
 		img.Parent = container.Image
 		img.Parent = container.Image
-		img.Container = container.Id
+		img.Container = container.ID
 		img.ContainerConfig = *container.Config
 		img.ContainerConfig = *container.Config
 	}
 	}
 	if err := graph.Register(layerData, layerData != nil, img); err != nil {
 	if err := graph.Register(layerData, layerData != nil, img); err != nil {
@@ -132,12 +133,12 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
 // Register imports a pre-existing image into the graph.
 // Register imports a pre-existing image into the graph.
 // FIXME: pass img as first argument
 // FIXME: pass img as first argument
 func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
 func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
-	if err := ValidateId(img.Id); err != nil {
+	if err := ValidateID(img.ID); err != nil {
 		return err
 		return err
 	}
 	}
 	// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
 	// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
-	if graph.Exists(img.Id) {
-		return fmt.Errorf("Image %s already exists", img.Id)
+	if graph.Exists(img.ID) {
+		return fmt.Errorf("Image %s already exists", img.ID)
 	}
 	}
 	tmp, err := graph.Mktemp("")
 	tmp, err := graph.Mktemp("")
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
@@ -148,12 +149,12 @@ func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
 		return err
 		return err
 	}
 	}
 	// Commit
 	// Commit
-	if err := os.Rename(tmp, graph.imageRoot(img.Id)); err != nil {
+	if err := os.Rename(tmp, graph.imageRoot(img.ID)); err != nil {
 		return err
 		return err
 	}
 	}
 	img.graph = graph
 	img.graph = graph
-	graph.idIndex.Add(img.Id)
-	graph.checksumLock[img.Id] = &sync.Mutex{}
+	graph.idIndex.Add(img.ID)
+	graph.checksumLock[img.ID] = &sync.Mutex{}
 	return nil
 	return nil
 }
 }
 
 
@@ -174,13 +175,14 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)", false), tmp.Root)
+	sf := utils.NewStreamFormatter(false)
+	return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, sf.FormatProgress("Buffering to disk", "%v/%v (%v)"), sf), tmp.Root)
 }
 }
 
 
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.
 func (graph *Graph) Mktemp(id string) (string, error) {
 func (graph *Graph) Mktemp(id string) (string, error) {
 	if id == "" {
 	if id == "" {
-		id = GenerateId()
+		id = GenerateID()
 	}
 	}
 	tmp, err := graph.tmp()
 	tmp, err := graph.tmp()
 	if err != nil {
 	if err != nil {
@@ -237,7 +239,7 @@ func (graph *Graph) Map() (map[string]*Image, error) {
 	}
 	}
 	images := make(map[string]*Image, len(all))
 	images := make(map[string]*Image, len(all))
 	for _, image := range all {
 	for _, image := range all {
-		images[image.Id] = image
+		images[image.ID] = image
 	}
 	}
 	return images, nil
 	return images, nil
 }
 }
@@ -280,10 +282,10 @@ func (graph *Graph) ByParent() (map[string][]*Image, error) {
 		if err != nil {
 		if err != nil {
 			return
 			return
 		}
 		}
-		if children, exists := byParent[parent.Id]; exists {
-			byParent[parent.Id] = []*Image{image}
+		if children, exists := byParent[parent.ID]; exists {
+			byParent[parent.ID] = []*Image{image}
 		} else {
 		} else {
-			byParent[parent.Id] = append(children, image)
+			byParent[parent.ID] = append(children, image)
 		}
 		}
 	})
 	})
 	return byParent, err
 	return byParent, err
@@ -300,8 +302,8 @@ func (graph *Graph) Heads() (map[string]*Image, error) {
 	err = graph.WalkAll(func(image *Image) {
 	err = graph.WalkAll(func(image *Image) {
 		// If it's not in the byParent lookup table, then
 		// If it's not in the byParent lookup table, then
 		// it's not a parent -> so it's a head!
 		// it's not a parent -> so it's a head!
-		if _, exists := byParent[image.Id]; !exists {
-			heads[image.Id] = image
+		if _, exists := byParent[image.ID]; !exists {
+			heads[image.ID] = image
 		}
 		}
 	})
 	})
 	return heads, err
 	return heads, err
@@ -324,11 +326,11 @@ func (graph *Graph) getStoredChecksums() (map[string]string, error) {
 }
 }
 
 
 func (graph *Graph) storeChecksums(checksums map[string]string) error {
 func (graph *Graph) storeChecksums(checksums map[string]string) error {
-	checksumJson, err := json.Marshal(checksums)
+	checksumJSON, err := json.Marshal(checksums)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJson, 0600); err != nil {
+	if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJSON, 0600); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil

+ 11 - 11
graph_test.go

@@ -34,14 +34,14 @@ func TestInterruptedRegister(t *testing.T) {
 	defer os.RemoveAll(graph.Root)
 	defer os.RemoveAll(graph.Root)
 	badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
 	badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
 	image := &Image{
 	image := &Image{
-		Id:      GenerateId(),
+		ID:      GenerateID(),
 		Comment: "testing",
 		Comment: "testing",
 		Created: time.Now(),
 		Created: time.Now(),
 	}
 	}
 	go graph.Register(badArchive, false, image)
 	go graph.Register(badArchive, false, image)
 	time.Sleep(200 * time.Millisecond)
 	time.Sleep(200 * time.Millisecond)
 	w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling)
 	w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling)
-	if _, err := graph.Get(image.Id); err == nil {
+	if _, err := graph.Get(image.ID); err == nil {
 		t.Fatal("Image should not exist after Register is interrupted")
 		t.Fatal("Image should not exist after Register is interrupted")
 	}
 	}
 	// Registering the same image again should succeed if the first register was interrupted
 	// Registering the same image again should succeed if the first register was interrupted
@@ -67,7 +67,7 @@ func TestGraphCreate(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if err := ValidateId(image.Id); err != nil {
+	if err := ValidateID(image.ID); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if image.Comment != "Testing" {
 	if image.Comment != "Testing" {
@@ -91,7 +91,7 @@ func TestRegister(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	image := &Image{
 	image := &Image{
-		Id:      GenerateId(),
+		ID:      GenerateID(),
 		Comment: "testing",
 		Comment: "testing",
 		Created: time.Now(),
 		Created: time.Now(),
 	}
 	}
@@ -104,11 +104,11 @@ func TestRegister(t *testing.T) {
 	} else if l := len(images); l != 1 {
 	} else if l := len(images); l != 1 {
 		t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
 		t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
 	}
 	}
-	if resultImg, err := graph.Get(image.Id); err != nil {
+	if resultImg, err := graph.Get(image.ID); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	} else {
 	} else {
-		if resultImg.Id != image.Id {
-			t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.Id, resultImg.Id)
+		if resultImg.ID != image.ID {
+			t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.ID, resultImg.ID)
 		}
 		}
 		if resultImg.Comment != image.Comment {
 		if resultImg.Comment != image.Comment {
 			t.Fatalf("Wrong image comment. Should be '%s', not '%s'", image.Comment, resultImg.Comment)
 			t.Fatalf("Wrong image comment. Should be '%s', not '%s'", image.Comment, resultImg.Comment)
@@ -156,7 +156,7 @@ func TestDeletePrefix(t *testing.T) {
 	graph := tempGraph(t)
 	graph := tempGraph(t)
 	defer os.RemoveAll(graph.Root)
 	defer os.RemoveAll(graph.Root)
 	img := createTestImage(graph, t)
 	img := createTestImage(graph, t)
-	if err := graph.Delete(utils.TruncateId(img.Id)); err != nil {
+	if err := graph.Delete(utils.TruncateID(img.ID)); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	assertNImages(graph, t, 0)
 	assertNImages(graph, t, 0)
@@ -187,7 +187,7 @@ func TestDelete(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	assertNImages(graph, t, 1)
 	assertNImages(graph, t, 1)
-	if err := graph.Delete(img.Id); err != nil {
+	if err := graph.Delete(img.ID); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	assertNImages(graph, t, 0)
 	assertNImages(graph, t, 0)
@@ -201,7 +201,7 @@ func TestDelete(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	assertNImages(graph, t, 2)
 	assertNImages(graph, t, 2)
-	if err := graph.Delete(img1.Id); err != nil {
+	if err := graph.Delete(img1.ID); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	assertNImages(graph, t, 1)
 	assertNImages(graph, t, 1)
@@ -216,7 +216,7 @@ func TestDelete(t *testing.T) {
 	if err := graph.Register(archive, false, img1); err != nil {
 	if err := graph.Register(archive, false, img1); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if err := graph.Delete(img1.Id); err != nil {
+	if err := graph.Delete(img1.ID); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	assertNImages(graph, t, 1)
 	assertNImages(graph, t, 1)

+ 14 - 6
hack/dockerbuilder/Dockerfile

@@ -1,23 +1,31 @@
 # This will build a container capable of producing an official binary build of docker and
 # This will build a container capable of producing an official binary build of docker and
 # uploading it to S3
 # uploading it to S3
+from	ubuntu:12.04
 maintainer	Solomon Hykes <solomon@dotcloud.com>
 maintainer	Solomon Hykes <solomon@dotcloud.com>
-from	ubuntu:12.10
+# Workaround the upstart issue
+run dpkg-divert --local --rename --add /sbin/initctl
+run ln -s /bin/true /sbin/initctl
+# Enable universe and gophers PPA
+run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q python-software-properties
+run	add-apt-repository "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) universe"
+run	add-apt-repository -y ppa:gophers/go/ubuntu
 run	apt-get update
 run	apt-get update
+# Packages required to checkout, build and upload docker
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
-# Packages required to checkout and build docker
 run	curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.linux-amd64.tar.gz
 run	curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.linux-amd64.tar.gz
 run	tar -C /usr/local -xzf /go.tar.gz
 run	tar -C /usr/local -xzf /go.tar.gz
-run	echo "export PATH=$PATH:/usr/local/go/bin" > /.bashrc
-run	echo "export PATH=$PATH:/usr/local/go/bin" > /.bash_profile
+run	echo "export PATH=/usr/local/go/bin:$PATH" > /.bashrc
+run	echo "export PATH=/usr/local/go/bin:$PATH" > /.bash_profile
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q build-essential
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q build-essential
 # Packages required to build an ubuntu package
 # Packages required to build an ubuntu package
+run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang-stable
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q debhelper
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q debhelper
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q autotools-dev
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q autotools-dev
-copy	fake_initctl	/usr/local/bin/initctl
 run	apt-get install -y -q devscripts
 run	apt-get install -y -q devscripts
-add	.	/src
+# Copy dockerbuilder files into the container
+add	.       /src
 run	cp /src/dockerbuilder /usr/local/bin/ && chmod +x /usr/local/bin/dockerbuilder
 run	cp /src/dockerbuilder /usr/local/bin/ && chmod +x /usr/local/bin/dockerbuilder
 run	cp /src/s3cfg /.s3cfg
 run	cp /src/s3cfg /.s3cfg
 cmd	["dockerbuilder"]
 cmd	["dockerbuilder"]

+ 2 - 1
hack/dockerbuilder/dockerbuilder

@@ -2,7 +2,7 @@
 set -x
 set -x
 set -e
 set -e
 
 
-export PATH=$PATH:/usr/local/go/bin
+export PATH=/usr/local/go/bin:$PATH
 
 
 PACKAGE=github.com/dotcloud/docker
 PACKAGE=github.com/dotcloud/docker
 
 
@@ -36,5 +36,6 @@ else
 fi
 fi
 
 
 if [ -z "$NO_UBUNTU" ]; then
 if [ -z "$NO_UBUNTU" ]; then
+	export PATH=`echo $PATH | sed 's#/usr/local/go/bin:##g'`
 	(cd packaging/ubuntu && make ubuntu)
 	(cd packaging/ubuntu && make ubuntu)
 fi
 fi

+ 0 - 3
hack/dockerbuilder/fake_initctl

@@ -1,3 +0,0 @@
-#!/bin/sh
-
-echo Whatever you say, man

+ 2 - 0
hack/infrastructure/MAINTAINERS

@@ -0,0 +1,2 @@
+Ken Cochrane <ken@dotcloud.com>
+Jerome Petazzoni <jerome@dotcloud.com>

+ 5 - 0
hack/infrastructure/README.md

@@ -0,0 +1,5 @@
+# Docker project infrastructure
+
+This directory holds all information about the technical infrastructure of the docker project; servers, dns, email, and all the corresponding tools and configuration.
+
+Obviously credentials should not be stored in this repo, but how to obtain and use them should be documented here.

+ 18 - 18
image.go

@@ -19,7 +19,7 @@ import (
 )
 )
 
 
 type Image struct {
 type Image struct {
-	Id              string    `json:"id"`
+	ID              string    `json:"id"`
 	Parent          string    `json:"parent,omitempty"`
 	Parent          string    `json:"parent,omitempty"`
 	Comment         string    `json:"comment,omitempty"`
 	Comment         string    `json:"comment,omitempty"`
 	Created         time.Time `json:"created"`
 	Created         time.Time `json:"created"`
@@ -28,6 +28,7 @@ type Image struct {
 	DockerVersion   string    `json:"docker_version,omitempty"`
 	DockerVersion   string    `json:"docker_version,omitempty"`
 	Author          string    `json:"author,omitempty"`
 	Author          string    `json:"author,omitempty"`
 	Config          *Config   `json:"config,omitempty"`
 	Config          *Config   `json:"config,omitempty"`
+	Architecture    string    `json:"architecture,omitempty"`
 	graph           *Graph
 	graph           *Graph
 	Size            int64
 	Size            int64
 	ParentSize      int64
 	ParentSize      int64
@@ -44,18 +45,17 @@ func LoadImage(root string) (*Image, error) {
 	if err := json.Unmarshal(jsonData, img); err != nil {
 	if err := json.Unmarshal(jsonData, img); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if err := ValidateId(img.Id); err != nil {
+	if err := ValidateID(img.ID); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	// Check that the filesystem layer exists
 	// Check that the filesystem layer exists
 	if stat, err := os.Stat(layerPath(root)); err != nil {
 	if stat, err := os.Stat(layerPath(root)); err != nil {
 		if os.IsNotExist(err) {
 		if os.IsNotExist(err) {
-			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.Id)
-		} else {
-			return nil, err
+			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
 		}
 		}
+		return nil, err
 	} else if !stat.IsDir() {
 	} else if !stat.IsDir() {
-		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.Id, layerPath(root))
+		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
 	}
 	}
 	return img, nil
 	return img, nil
 }
 }
@@ -63,7 +63,7 @@ func LoadImage(root string) (*Image, error) {
 func StoreImage(img *Image, layerData Archive, root string, store bool) error {
 func StoreImage(img *Image, layerData Archive, root string, store bool) error {
 	// Check that root doesn't already exist
 	// Check that root doesn't already exist
 	if _, err := os.Stat(root); err == nil {
 	if _, err := os.Stat(root); err == nil {
-		return fmt.Errorf("Image %s already exists", img.Id)
+		return fmt.Errorf("Image %s already exists", img.ID)
 	} else if !os.IsNotExist(err) {
 	} else if !os.IsNotExist(err) {
 		return err
 		return err
 	}
 	}
@@ -195,11 +195,11 @@ func (image *Image) Changes(rw string) ([]Change, error) {
 	return Changes(layers, rw)
 	return Changes(layers, rw)
 }
 }
 
 
-func (image *Image) ShortId() string {
-	return utils.TruncateId(image.Id)
+func (image *Image) ShortID() string {
+	return utils.TruncateID(image.ID)
 }
 }
 
 
-func ValidateId(id string) error {
+func ValidateID(id string) error {
 	if id == "" {
 	if id == "" {
 		return fmt.Errorf("Image id can't be empty")
 		return fmt.Errorf("Image id can't be empty")
 	}
 	}
@@ -209,7 +209,7 @@ func ValidateId(id string) error {
 	return nil
 	return nil
 }
 }
 
 
-func GenerateId() string {
+func GenerateID() string {
 	id := make([]byte, 32)
 	id := make([]byte, 32)
 	_, err := io.ReadFull(rand.Reader, id)
 	_, err := io.ReadFull(rand.Reader, id)
 	if err != nil {
 	if err != nil {
@@ -255,7 +255,7 @@ func (img *Image) layers() ([]string, error) {
 		return nil, e
 		return nil, e
 	}
 	}
 	if len(list) == 0 {
 	if len(list) == 0 {
-		return nil, fmt.Errorf("No layer found for image %s\n", img.Id)
+		return nil, fmt.Errorf("No layer found for image %s\n", img.ID)
 	}
 	}
 	return list, nil
 	return list, nil
 }
 }
@@ -290,7 +290,7 @@ func (img *Image) root() (string, error) {
 	if img.graph == nil {
 	if img.graph == nil {
 		return "", fmt.Errorf("Can't lookup root of unregistered image")
 		return "", fmt.Errorf("Can't lookup root of unregistered image")
 	}
 	}
-	return img.graph.imageRoot(img.Id), nil
+	return img.graph.imageRoot(img.ID), nil
 }
 }
 
 
 // Return the path of an image's layer
 // Return the path of an image's layer
@@ -303,8 +303,8 @@ func (img *Image) layer() (string, error) {
 }
 }
 
 
 func (img *Image) Checksum() (string, error) {
 func (img *Image) Checksum() (string, error) {
-	img.graph.checksumLock[img.Id].Lock()
-	defer img.graph.checksumLock[img.Id].Unlock()
+	img.graph.checksumLock[img.ID].Lock()
+	defer img.graph.checksumLock[img.ID].Unlock()
 
 
 	root, err := img.root()
 	root, err := img.root()
 	if err != nil {
 	if err != nil {
@@ -315,7 +315,7 @@ func (img *Image) Checksum() (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	if checksum, ok := checksums[img.Id]; ok {
+	if checksum, ok := checksums[img.ID]; ok {
 		return checksum, nil
 		return checksum, nil
 	}
 	}
 
 
@@ -366,7 +366,7 @@ func (img *Image) Checksum() (string, error) {
 		return "", err
 		return "", err
 	}
 	}
 
 
-	checksums[img.Id] = hash
+	checksums[img.ID] = hash
 
 
 	// Dump the checksums to disc
 	// Dump the checksums to disc
 	if err := img.graph.storeChecksums(checksums); err != nil {
 	if err := img.graph.storeChecksums(checksums); err != nil {
@@ -386,7 +386,7 @@ func (img *Image) getVirtualSize(size int64) int64 {
 }
 }
 
 
 // Build an Image object from raw json data
 // Build an Image object from raw json data
-func NewImgJson(src []byte) (*Image, error) {
+func NewImgJSON(src []byte) (*Image, error) {
 	ret := &Image{}
 	ret := &Image{}
 
 
 	utils.Debugf("Json string: {%s}\n", src)
 	utils.Debugf("Json string: {%s}\n", src)

+ 1 - 1
lxc_template.go

@@ -19,7 +19,7 @@ lxc.network.flags = up
 lxc.network.link = {{.NetworkSettings.Bridge}}
 lxc.network.link = {{.NetworkSettings.Bridge}}
 lxc.network.name = eth0
 lxc.network.name = eth0
 lxc.network.mtu = 1500
 lxc.network.mtu = 1500
-lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}}
+lxc.network.ipv4 = {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}
 
 
 # root filesystem
 # root filesystem
 {{$ROOTFS := .RootfsPath}}
 {{$ROOTFS := .RootfsPath}}

+ 7 - 8
network.go

@@ -52,7 +52,7 @@ func ipToInt(ip net.IP) int32 {
 }
 }
 
 
 // Converts 32 bit integer into a 4 bytes IP address
 // Converts 32 bit integer into a 4 bytes IP address
-func intToIp(n int32) net.IP {
+func intToIP(n int32) net.IP {
 	b := make([]byte, 4)
 	b := make([]byte, 4)
 	binary.BigEndian.PutUint32(b, uint32(n))
 	binary.BigEndian.PutUint32(b, uint32(n))
 	return net.IP(b)
 	return net.IP(b)
@@ -132,9 +132,8 @@ func CreateBridgeIface(ifaceName string) error {
 	}
 	}
 	if ifaceAddr == "" {
 	if ifaceAddr == "" {
 		return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName)
 		return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName)
-	} else {
-		utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
 	}
 	}
+	utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
 
 
 	if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil {
 	if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil {
 		return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output)
 		return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output)
@@ -258,7 +257,7 @@ func proxy(listener net.Listener, proto, address string) error {
 		utils.Debugf("Connected to backend, splicing")
 		utils.Debugf("Connected to backend, splicing")
 		splice(src, dst)
 		splice(src, dst)
 	}
 	}
-	return nil
+	panic("Unreachable")
 }
 }
 
 
 func halfSplice(dst, src net.Conn) error {
 func halfSplice(dst, src net.Conn) error {
@@ -398,7 +397,7 @@ func (alloc *IPAllocator) run() {
 			}
 			}
 		}
 		}
 
 
-		ip := allocatedIP{ip: intToIp(newNum)}
+		ip := allocatedIP{ip: intToIP(newNum)}
 		if inUse {
 		if inUse {
 			ip.err = errors.New("No unallocated IP available")
 			ip.err = errors.New("No unallocated IP available")
 		}
 		}
@@ -465,11 +464,11 @@ func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 	// Allocate a random port if Frontend==0
 	// Allocate a random port if Frontend==0
-	if extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend); err != nil {
+	extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend)
+	if err != nil {
 		return nil, err
 		return nil, err
-	} else {
-		nat.Frontend = extPort
 	}
 	}
+	nat.Frontend = extPort
 	if err := iface.manager.portMapper.Map(nat.Frontend, net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}); err != nil {
 	if err := iface.manager.portMapper.Map(nat.Frontend, net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}); err != nil {
 		iface.manager.portAllocator.Release(nat.Frontend)
 		iface.manager.portAllocator.Release(nat.Frontend)
 		return nil, err
 		return nil, err

+ 1 - 1
network_test.go

@@ -137,7 +137,7 @@ func TestConversion(t *testing.T) {
 	if i == 0 {
 	if i == 0 {
 		t.Fatal("converted to zero")
 		t.Fatal("converted to zero")
 	}
 	}
-	conv := intToIp(i)
+	conv := intToIP(i)
 	if !ip.Equal(conv) {
 	if !ip.Equal(conv) {
 		t.Error(conv.String())
 		t.Error(conv.String())
 	}
 	}

+ 11 - 0
packaging/debian/changelog

@@ -1,3 +1,14 @@
+lxc-docker (0.3.4-1) UNRELEASED; urgency=low
+  - Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
+  - Builder: 'docker build -t FOO' applies the tag FOO to the newly built container.
+  - Runtime: interactive TTYs correctly handle window resize
+  - Runtime: fix how configuration is merged between layers
+  - Remote API: split stdout and stderr on 'docker run'
+  - Remote API: optionally listen on a different IP and port (use at your own risk)
+  - Documentation: improved install instructions.
+
+ -- dotCloud <ops@dotcloud.com>  Thu, 30 May 2013 00:00:00 -0700
+
 lxc-docker (0.3.2-1) UNRELEASED; urgency=low
 lxc-docker (0.3.2-1) UNRELEASED; urgency=low
   - Runtime: Store the actual archive on commit
   - Runtime: Store the actual archive on commit
   - Registry: Improve the checksum process
   - Registry: Improve the checksum process

+ 19 - 0
packaging/ubuntu/changelog

@@ -1,3 +1,22 @@
+lxc-docker (0.4.0-1) precise; urgency=low
+  - Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
+  - Introducing Remote API: control Docker programmatically using a simple HTTP/json API
+  - Runtime: various reliability and usability improvements
+
+ -- dotCloud <ops@dotcloud.com>  Mon, 03 Jun 2013 00:00:00 -0700
+
+lxc-docker (0.3.4-1) precise; urgency=low
+  - Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
+  - Builder: 'docker build -t FOO' applies the tag FOO to the newly built container.
+  - Runtime: interactive TTYs correctly handle window resize
+  - Runtime: fix how configuration is merged between layers
+  - Remote API: split stdout and stderr on 'docker run'
+  - Remote API: optionally listen on a different IP and port (use at your own risk)
+  - Documentation: improved install instructions.
+
+ -- dotCloud <ops@dotcloud.com>  Thu, 30 May 2013 00:00:00 -0700
+
+
 lxc-docker (0.3.3-1) precise; urgency=low
 lxc-docker (0.3.3-1) precise; urgency=low
   - Registry: Fix push regression
   - Registry: Fix push regression
   - Various bugfixes
   - Various bugfixes

+ 1 - 2
packaging/ubuntu/docker.upstart

@@ -5,6 +5,5 @@ stop on starting rc RUNLEVEL=[016]
 respawn
 respawn
 
 
 script
 script
-    # FIXME: docker should not depend on the system having en_US.UTF-8
-    LC_ALL='en_US.UTF-8' /usr/bin/docker -d
+    /usr/bin/docker -d
 end script
 end script

+ 41 - 27
registry/registry.go

@@ -15,7 +15,7 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-var ErrAlreadyExists error = errors.New("Image already exists")
+var ErrAlreadyExists = errors.New("Image already exists")
 
 
 func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) {
 func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) {
 	for _, cookie := range c.Jar.Cookies(req.URL) {
 	for _, cookie := range c.Jar.Cookies(req.URL) {
@@ -64,7 +64,11 @@ func (r *Registry) LookupRemoteImage(imgId, registry string, authConfig *auth.Au
 	}
 	}
 	req.SetBasicAuth(authConfig.Username, authConfig.Password)
 	req.SetBasicAuth(authConfig.Username, authConfig.Password)
 	res, err := rt.RoundTrip(req)
 	res, err := rt.RoundTrip(req)
-	return err == nil && res.StatusCode == 307
+	if err != nil {
+		return false
+	}
+	res.Body.Close()
+	return res.StatusCode == 307
 }
 }
 
 
 func (r *Registry) getImagesInRepository(repository string, authConfig *auth.AuthConfig) ([]map[string]string, error) {
 func (r *Registry) getImagesInRepository(repository string, authConfig *auth.AuthConfig) ([]map[string]string, error) {
@@ -103,8 +107,8 @@ func (r *Registry) getImagesInRepository(repository string, authConfig *auth.Aut
 
 
 // Retrieve an image from the Registry.
 // Retrieve an image from the Registry.
 // Returns the Image object as well as the layer as an Archive (io.Reader)
 // Returns the Image object as well as the layer as an Archive (io.Reader)
-func (r *Registry) GetRemoteImageJson(imgId, registry string, token []string) ([]byte, error) {
-	// Get the Json
+func (r *Registry) GetRemoteImageJSON(imgId, registry string, token []string) ([]byte, error) {
+	// Get the JSON
 	req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil)
 	req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("Failed to download json: %s", err)
 		return nil, fmt.Errorf("Failed to download json: %s", err)
@@ -152,21 +156,24 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [
 		}
 		}
 		req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
 		req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
 		res, err := r.client.Do(req)
 		res, err := r.client.Do(req)
-		defer res.Body.Close()
 		utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint)
 		utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint)
-		if err != nil || (res.StatusCode != 200 && res.StatusCode != 404) {
+		if err != nil {
+			return nil, err
+		}
+		defer res.Body.Close()
+
+		if res.StatusCode != 200 && res.StatusCode != 404 {
 			continue
 			continue
 		} else if res.StatusCode == 404 {
 		} else if res.StatusCode == 404 {
 			return nil, fmt.Errorf("Repository not found")
 			return nil, fmt.Errorf("Repository not found")
 		}
 		}
 
 
 		result := make(map[string]string)
 		result := make(map[string]string)
-
-		rawJson, err := ioutil.ReadAll(res.Body)
+		rawJSON, err := ioutil.ReadAll(res.Body)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		if err := json.Unmarshal(rawJson, &result); err != nil {
+		if err := json.Unmarshal(rawJSON, &result); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 		return result, nil
 		return result, nil
@@ -212,19 +219,19 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
 		return nil, fmt.Errorf("Index response didn't contain any endpoints")
 		return nil, fmt.Errorf("Index response didn't contain any endpoints")
 	}
 	}
 
 
-	checksumsJson, err := ioutil.ReadAll(res.Body)
+	checksumsJSON, err := ioutil.ReadAll(res.Body)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	remoteChecksums := []*ImgData{}
 	remoteChecksums := []*ImgData{}
-	if err := json.Unmarshal(checksumsJson, &remoteChecksums); err != nil {
+	if err := json.Unmarshal(checksumsJSON, &remoteChecksums); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	// Forge a better object from the retrieved data
 	// Forge a better object from the retrieved data
 	imgsData := make(map[string]*ImgData)
 	imgsData := make(map[string]*ImgData)
 	for _, elem := range remoteChecksums {
 	for _, elem := range remoteChecksums {
-		imgsData[elem.Id] = elem
+		imgsData[elem.ID] = elem
 	}
 	}
 
 
 	return &RepositoryData{
 	return &RepositoryData{
@@ -235,10 +242,10 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
 }
 }
 
 
 // Push a local image to the registry
 // Push a local image to the registry
-func (r *Registry) PushImageJsonRegistry(imgData *ImgData, jsonRaw []byte, registry string, token []string) error {
+func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, registry string, token []string) error {
 	registry = "https://" + registry + "/v1"
 	registry = "https://" + registry + "/v1"
 	// FIXME: try json with UTF8
 	// FIXME: try json with UTF8
-	req, err := http.NewRequest("PUT", registry+"/images/"+imgData.Id+"/json", strings.NewReader(string(jsonRaw)))
+	req, err := http.NewRequest("PUT", registry+"/images/"+imgData.ID+"/json", strings.NewReader(string(jsonRaw)))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -246,7 +253,7 @@ func (r *Registry) PushImageJsonRegistry(imgData *ImgData, jsonRaw []byte, regis
 	req.Header.Set("Authorization", "Token "+strings.Join(token, ","))
 	req.Header.Set("Authorization", "Token "+strings.Join(token, ","))
 	req.Header.Set("X-Docker-Checksum", imgData.Checksum)
 	req.Header.Set("X-Docker-Checksum", imgData.Checksum)
 
 
-	utils.Debugf("Setting checksum for %s: %s", imgData.Id, imgData.Checksum)
+	utils.Debugf("Setting checksum for %s: %s", imgData.ID, imgData.Checksum)
 	res, err := doWithCookies(r.client, req)
 	res, err := doWithCookies(r.client, req)
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("Failed to upload metadata: %s", err)
 		return fmt.Errorf("Failed to upload metadata: %s", err)
@@ -321,8 +328,8 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token
 	return nil
 	return nil
 }
 }
 
 
-func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) {
-	imgListJson, err := json.Marshal(imgList)
+func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) {
+	imgListJSON, err := json.Marshal(imgList)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -331,14 +338,14 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
 		suffix = "images"
 		suffix = "images"
 	}
 	}
 
 
-	utils.Debugf("Image list pushed to index:\n%s\n", imgListJson)
+	utils.Debugf("Image list pushed to index:\n%s\n", imgListJSON)
 
 
-	req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJson))
+	req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJSON))
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
 	req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
-	req.ContentLength = int64(len(imgListJson))
+	req.ContentLength = int64(len(imgListJSON))
 	req.Header.Set("X-Docker-Token", "true")
 	req.Header.Set("X-Docker-Token", "true")
 
 
 	res, err := r.client.Do(req)
 	res, err := r.client.Do(req)
@@ -350,12 +357,12 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
 	// Redirect if necessary
 	// Redirect if necessary
 	for res.StatusCode >= 300 && res.StatusCode < 400 {
 	for res.StatusCode >= 300 && res.StatusCode < 400 {
 		utils.Debugf("Redirected to %s\n", res.Header.Get("Location"))
 		utils.Debugf("Redirected to %s\n", res.Header.Get("Location"))
-		req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJson))
+		req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJSON))
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 		req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
 		req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
-		req.ContentLength = int64(len(imgListJson))
+		req.ContentLength = int64(len(imgListJSON))
 		req.Header.Set("X-Docker-Token", "true")
 		req.Header.Set("X-Docker-Token", "true")
 
 
 		res, err = r.client.Do(req)
 		res, err = r.client.Do(req)
@@ -389,11 +396,11 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
 	}
 	}
 	if validate {
 	if validate {
 		if res.StatusCode != 204 {
 		if res.StatusCode != 204 {
-			if errBody, err := ioutil.ReadAll(res.Body); err != nil {
+			errBody, err := ioutil.ReadAll(res.Body)
+			if err != nil {
 				return nil, err
 				return nil, err
-			} else {
-				return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody)
 			}
 			}
+			return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody)
 		}
 		}
 	}
 	}
 
 
@@ -456,7 +463,7 @@ type RepositoryData struct {
 }
 }
 
 
 type ImgData struct {
 type ImgData struct {
-	Id       string `json:"id"`
+	ID       string `json:"id"`
 	Checksum string `json:"checksum,omitempty"`
 	Checksum string `json:"checksum,omitempty"`
 	Tag      string `json:",omitempty"`
 	Tag      string `json:",omitempty"`
 }
 }
@@ -470,9 +477,16 @@ func NewRegistry(root string) *Registry {
 	// If the auth file does not exist, keep going
 	// If the auth file does not exist, keep going
 	authConfig, _ := auth.LoadConfig(root)
 	authConfig, _ := auth.LoadConfig(root)
 
 
+	httpTransport := &http.Transport{
+		DisableKeepAlives: true,
+		Proxy: http.ProxyFromEnvironment,
+	}
+
 	r := &Registry{
 	r := &Registry{
 		authConfig: authConfig,
 		authConfig: authConfig,
-		client:     &http.Client{},
+		client: &http.Client{
+			Transport: httpTransport,
+		},
 	}
 	}
 	r.client.Jar = cookiejar.NewCookieJar()
 	r.client.Jar = cookiejar.NewCookieJar()
 	return r
 	return r

+ 31 - 31
runtime.go

@@ -51,7 +51,7 @@ func (runtime *Runtime) List() []*Container {
 func (runtime *Runtime) getContainerElement(id string) *list.Element {
 func (runtime *Runtime) getContainerElement(id string) *list.Element {
 	for e := runtime.containers.Front(); e != nil; e = e.Next() {
 	for e := runtime.containers.Front(); e != nil; e = e.Next() {
 		container := e.Value.(*Container)
 		container := e.Value.(*Container)
-		if container.Id == id {
+		if container.ID == id {
 			return e
 			return e
 		}
 		}
 	}
 	}
@@ -83,8 +83,8 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
 	if err := container.FromDisk(); err != nil {
 	if err := container.FromDisk(); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if container.Id != id {
-		return container, fmt.Errorf("Container %s is stored at %s", container.Id, id)
+	if container.ID != id {
+		return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
 	}
 	}
 	if container.State.Running {
 	if container.State.Running {
 		container.State.Ghost = true
 		container.State.Ghost = true
@@ -95,12 +95,12 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
 	return container, nil
 	return container, nil
 }
 }
 
 
-// Register makes a container object usable by the runtime as <container.Id>
+// Register makes a container object usable by the runtime as <container.ID>
 func (runtime *Runtime) Register(container *Container) error {
 func (runtime *Runtime) Register(container *Container) error {
-	if container.runtime != nil || runtime.Exists(container.Id) {
+	if container.runtime != nil || runtime.Exists(container.ID) {
 		return fmt.Errorf("Container is already loaded")
 		return fmt.Errorf("Container is already loaded")
 	}
 	}
-	if err := validateId(container.Id); err != nil {
+	if err := validateID(container.ID); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -123,7 +123,7 @@ func (runtime *Runtime) Register(container *Container) error {
 	}
 	}
 	// done
 	// done
 	runtime.containers.PushBack(container)
 	runtime.containers.PushBack(container)
-	runtime.idIndex.Add(container.Id)
+	runtime.idIndex.Add(container.ID)
 
 
 	// When we actually restart, Start() do the monitoring.
 	// When we actually restart, Start() do the monitoring.
 	// However, when we simply 'reattach', we have to restart a monitor
 	// However, when we simply 'reattach', we have to restart a monitor
@@ -133,25 +133,25 @@ func (runtime *Runtime) Register(container *Container) error {
 	//        if so, then we need to restart monitor and init a new lock
 	//        if so, then we need to restart monitor and init a new lock
 	// If the container is supposed to be running, make sure of it
 	// If the container is supposed to be running, make sure of it
 	if container.State.Running {
 	if container.State.Running {
-		if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
+		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
+		if err != nil {
 			return err
 			return err
-		} else {
-			if !strings.Contains(string(output), "RUNNING") {
-				utils.Debugf("Container %s was supposed to be running be is not.", container.Id)
-				if runtime.autoRestart {
-					utils.Debugf("Restarting")
-					container.State.Ghost = false
-					container.State.setStopped(0)
-					if err := container.Start(); err != nil {
-						return err
-					}
-					nomonitor = true
-				} else {
-					utils.Debugf("Marking as stopped")
-					container.State.setStopped(-127)
-					if err := container.ToDisk(); err != nil {
-						return err
-					}
+		}
+		if !strings.Contains(string(output), "RUNNING") {
+			utils.Debugf("Container %s was supposed to be running be is not.", container.ID)
+			if runtime.autoRestart {
+				utils.Debugf("Restarting")
+				container.State.Ghost = false
+				container.State.setStopped(0)
+				if err := container.Start(); err != nil {
+					return err
+				}
+				nomonitor = true
+			} else {
+				utils.Debugf("Marking as stopped")
+				container.State.setStopped(-127)
+				if err := container.ToDisk(); err != nil {
+					return err
 				}
 				}
 			}
 			}
 		}
 		}
@@ -182,9 +182,9 @@ func (runtime *Runtime) Destroy(container *Container) error {
 		return fmt.Errorf("The given container is <nil>")
 		return fmt.Errorf("The given container is <nil>")
 	}
 	}
 
 
-	element := runtime.getContainerElement(container.Id)
+	element := runtime.getContainerElement(container.ID)
 	if element == nil {
 	if element == nil {
-		return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.Id)
+		return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
 	}
 	}
 
 
 	if err := container.Stop(3); err != nil {
 	if err := container.Stop(3); err != nil {
@@ -194,14 +194,14 @@ func (runtime *Runtime) Destroy(container *Container) error {
 		return err
 		return err
 	} else if mounted {
 	} else if mounted {
 		if err := container.Unmount(); err != nil {
 		if err := container.Unmount(); err != nil {
-			return fmt.Errorf("Unable to unmount container %v: %v", container.Id, err)
+			return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
 		}
 		}
 	}
 	}
 	// Deregister the container before removing its directory, to avoid race conditions
 	// Deregister the container before removing its directory, to avoid race conditions
-	runtime.idIndex.Delete(container.Id)
+	runtime.idIndex.Delete(container.ID)
 	runtime.containers.Remove(element)
 	runtime.containers.Remove(element)
 	if err := os.RemoveAll(container.root); err != nil {
 	if err := os.RemoveAll(container.root); err != nil {
-		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.Id, err)
+		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -218,7 +218,7 @@ func (runtime *Runtime) restore() error {
 			utils.Debugf("Failed to load container %v: %v", id, err)
 			utils.Debugf("Failed to load container %v: %v", id, err)
 			continue
 			continue
 		}
 		}
-		utils.Debugf("Loaded container %v", container.Id)
+		utils.Debugf("Loaded container %v", container.ID)
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 26 - 26
runtime_test.go

@@ -68,7 +68,7 @@ func init() {
 		runtime: runtime,
 		runtime: runtime,
 	}
 	}
 	// Retrieve the Image
 	// Retrieve the Image
-	if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, false); err != nil {
+	if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false)); err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
 }
 }
@@ -120,7 +120,7 @@ func TestRuntimeCreate(t *testing.T) {
 	builder := NewBuilder(runtime)
 	builder := NewBuilder(runtime)
 
 
 	container, err := builder.Create(&Config{
 	container, err := builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 		Cmd:   []string{"ls", "-al"},
 	},
 	},
 	)
 	)
@@ -140,29 +140,29 @@ func TestRuntimeCreate(t *testing.T) {
 	}
 	}
 
 
 	// Make sure the container List() returns is the right one
 	// Make sure the container List() returns is the right one
-	if runtime.List()[0].Id != container.Id {
+	if runtime.List()[0].ID != container.ID {
 		t.Errorf("Unexpected container %v returned by List", runtime.List()[0])
 		t.Errorf("Unexpected container %v returned by List", runtime.List()[0])
 	}
 	}
 
 
 	// Make sure we can get the container with Get()
 	// Make sure we can get the container with Get()
-	if runtime.Get(container.Id) == nil {
+	if runtime.Get(container.ID) == nil {
 		t.Errorf("Unable to get newly created container")
 		t.Errorf("Unable to get newly created container")
 	}
 	}
 
 
 	// Make sure it is the right container
 	// Make sure it is the right container
-	if runtime.Get(container.Id) != container {
+	if runtime.Get(container.ID) != container {
 		t.Errorf("Get() returned the wrong container")
 		t.Errorf("Get() returned the wrong container")
 	}
 	}
 
 
 	// Make sure Exists returns it as existing
 	// Make sure Exists returns it as existing
-	if !runtime.Exists(container.Id) {
+	if !runtime.Exists(container.ID) {
 		t.Errorf("Exists() returned false for a newly created container")
 		t.Errorf("Exists() returned false for a newly created container")
 	}
 	}
 
 
 	// Make sure crete with bad parameters returns an error
 	// Make sure crete with bad parameters returns an error
 	_, err = builder.Create(
 	_, err = builder.Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 		},
 		},
 	)
 	)
 	if err == nil {
 	if err == nil {
@@ -171,7 +171,7 @@ func TestRuntimeCreate(t *testing.T) {
 
 
 	_, err = builder.Create(
 	_, err = builder.Create(
 		&Config{
 		&Config{
-			Image: GetTestImage(runtime).Id,
+			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{},
 			Cmd:   []string{},
 		},
 		},
 	)
 	)
@@ -187,7 +187,7 @@ func TestDestroy(t *testing.T) {
 	}
 	}
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 		Cmd:   []string{"ls", "-al"},
 	},
 	},
 	)
 	)
@@ -210,7 +210,7 @@ func TestDestroy(t *testing.T) {
 	}
 	}
 
 
 	// Make sure runtime.Get() refuses to return the unexisting container
 	// Make sure runtime.Get() refuses to return the unexisting container
-	if runtime.Get(container.Id) != nil {
+	if runtime.Get(container.ID) != nil {
 		t.Errorf("Unable to get newly created container")
 		t.Errorf("Unable to get newly created container")
 	}
 	}
 
 
@@ -237,7 +237,7 @@ func TestGet(t *testing.T) {
 	builder := NewBuilder(runtime)
 	builder := NewBuilder(runtime)
 
 
 	container1, err := builder.Create(&Config{
 	container1, err := builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 		Cmd:   []string{"ls", "-al"},
 	},
 	},
 	)
 	)
@@ -247,7 +247,7 @@ func TestGet(t *testing.T) {
 	defer runtime.Destroy(container1)
 	defer runtime.Destroy(container1)
 
 
 	container2, err := builder.Create(&Config{
 	container2, err := builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 		Cmd:   []string{"ls", "-al"},
 	},
 	},
 	)
 	)
@@ -257,7 +257,7 @@ func TestGet(t *testing.T) {
 	defer runtime.Destroy(container2)
 	defer runtime.Destroy(container2)
 
 
 	container3, err := builder.Create(&Config{
 	container3, err := builder.Create(&Config{
-		Image: GetTestImage(runtime).Id,
+		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 		Cmd:   []string{"ls", "-al"},
 	},
 	},
 	)
 	)
@@ -266,16 +266,16 @@ func TestGet(t *testing.T) {
 	}
 	}
 	defer runtime.Destroy(container3)
 	defer runtime.Destroy(container3)
 
 
-	if runtime.Get(container1.Id) != container1 {
-		t.Errorf("Get(test1) returned %v while expecting %v", runtime.Get(container1.Id), container1)
+	if runtime.Get(container1.ID) != container1 {
+		t.Errorf("Get(test1) returned %v while expecting %v", runtime.Get(container1.ID), container1)
 	}
 	}
 
 
-	if runtime.Get(container2.Id) != container2 {
-		t.Errorf("Get(test2) returned %v while expecting %v", runtime.Get(container2.Id), container2)
+	if runtime.Get(container2.ID) != container2 {
+		t.Errorf("Get(test2) returned %v while expecting %v", runtime.Get(container2.ID), container2)
 	}
 	}
 
 
-	if runtime.Get(container3.Id) != container3 {
-		t.Errorf("Get(test3) returned %v while expecting %v", runtime.Get(container3.Id), container3)
+	if runtime.Get(container3.ID) != container3 {
+		t.Errorf("Get(test3) returned %v while expecting %v", runtime.Get(container3.ID), container3)
 	}
 	}
 
 
 }
 }
@@ -283,7 +283,7 @@ func TestGet(t *testing.T) {
 func findAvailalblePort(runtime *Runtime, port int) (*Container, error) {
 func findAvailalblePort(runtime *Runtime, port int) (*Container, error) {
 	strPort := strconv.Itoa(port)
 	strPort := strconv.Itoa(port)
 	container, err := NewBuilder(runtime).Create(&Config{
 	container, err := NewBuilder(runtime).Create(&Config{
-		Image:     GetTestImage(runtime).Id,
+		Image:     GetTestImage(runtime).ID,
 		Cmd:       []string{"sh", "-c", "echo well hello there | nc -l -p " + strPort},
 		Cmd:       []string{"sh", "-c", "echo well hello there | nc -l -p " + strPort},
 		PortSpecs: []string{strPort},
 		PortSpecs: []string{strPort},
 	},
 	},
@@ -379,7 +379,7 @@ func TestRestore(t *testing.T) {
 
 
 	// Create a container with one instance of docker
 	// Create a container with one instance of docker
 	container1, err := builder.Create(&Config{
 	container1, err := builder.Create(&Config{
-		Image: GetTestImage(runtime1).Id,
+		Image: GetTestImage(runtime1).ID,
 		Cmd:   []string{"ls", "-al"},
 		Cmd:   []string{"ls", "-al"},
 	},
 	},
 	)
 	)
@@ -390,7 +390,7 @@ func TestRestore(t *testing.T) {
 
 
 	// Create a second container meant to be killed
 	// Create a second container meant to be killed
 	container2, err := builder.Create(&Config{
 	container2, err := builder.Create(&Config{
-		Image:     GetTestImage(runtime1).Id,
+		Image:     GetTestImage(runtime1).ID,
 		Cmd:       []string{"/bin/cat"},
 		Cmd:       []string{"/bin/cat"},
 		OpenStdin: true,
 		OpenStdin: true,
 	},
 	},
@@ -406,7 +406,7 @@ func TestRestore(t *testing.T) {
 	}
 	}
 
 
 	if !container2.State.Running {
 	if !container2.State.Running {
-		t.Fatalf("Container %v should appear as running but isn't", container2.Id)
+		t.Fatalf("Container %v should appear as running but isn't", container2.ID)
 	}
 	}
 
 
 	// Simulate a crash/manual quit of dockerd: process dies, states stays 'Running'
 	// Simulate a crash/manual quit of dockerd: process dies, states stays 'Running'
@@ -426,7 +426,7 @@ func TestRestore(t *testing.T) {
 	}
 	}
 
 
 	if !container2.State.Running {
 	if !container2.State.Running {
-		t.Fatalf("Container %v should appear as running but isn't", container2.Id)
+		t.Fatalf("Container %v should appear as running but isn't", container2.ID)
 	}
 	}
 
 
 	// Here are are simulating a docker restart - that is, reloading all containers
 	// Here are are simulating a docker restart - that is, reloading all containers
@@ -442,14 +442,14 @@ func TestRestore(t *testing.T) {
 	runningCount := 0
 	runningCount := 0
 	for _, c := range runtime2.List() {
 	for _, c := range runtime2.List() {
 		if c.State.Running {
 		if c.State.Running {
-			t.Errorf("Running container found: %v (%v)", c.Id, c.Path)
+			t.Errorf("Running container found: %v (%v)", c.ID, c.Path)
 			runningCount++
 			runningCount++
 		}
 		}
 	}
 	}
 	if runningCount != 0 {
 	if runningCount != 0 {
 		t.Fatalf("Expected 0 container alive, %d found", runningCount)
 		t.Fatalf("Expected 0 container alive, %d found", runningCount)
 	}
 	}
-	container3 := runtime2.Get(container1.Id)
+	container3 := runtime2.Get(container1.ID)
 	if container3 == nil {
 	if container3 == nil {
 		t.Fatal("Unable to Get container")
 		t.Fatal("Unable to Get container")
 	}
 	}

+ 104 - 100
server.go

@@ -16,8 +16,12 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-func (srv *Server) DockerVersion() ApiVersion {
-	return ApiVersion{VERSION, GIT_COMMIT, srv.runtime.capabilities.MemoryLimit, srv.runtime.capabilities.SwapLimit}
+func (srv *Server) DockerVersion() APIVersion {
+	return APIVersion{
+		Version:   VERSION,
+		GitCommit: GITCOMMIT,
+		GoVersion: runtime.Version(),
+	}
 }
 }
 
 
 func (srv *Server) ContainerKill(name string) error {
 func (srv *Server) ContainerKill(name string) error {
@@ -48,16 +52,16 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
 	return fmt.Errorf("No such container: %s", name)
 	return fmt.Errorf("No such container: %s", name)
 }
 }
 
 
-func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) {
+func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
 
 
 	results, err := registry.NewRegistry(srv.runtime.root).SearchRepositories(term)
 	results, err := registry.NewRegistry(srv.runtime.root).SearchRepositories(term)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	var outs []ApiSearch
+	var outs []APISearch
 	for _, repo := range results.Results {
 	for _, repo := range results.Results {
-		var out ApiSearch
+		var out APISearch
 		out.Description = repo["description"]
 		out.Description = repo["description"]
 		if len(out.Description) > 45 {
 		if len(out.Description) > 45 {
 			out.Description = utils.Trunc(out.Description, 42) + "..."
 			out.Description = utils.Trunc(out.Description, 42) + "..."
@@ -68,7 +72,7 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) {
 	return outs, nil
 	return outs, nil
 }
 }
 
 
-func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, error) {
+func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) (string, error) {
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
 	img, err := srv.runtime.repositories.LookupImage(name)
 	img, err := srv.runtime.repositories.LookupImage(name)
 	if err != nil {
 	if err != nil {
@@ -81,7 +85,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e
 	}
 	}
 	defer file.Body.Close()
 	defer file.Body.Close()
 
 
-	config, _, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, srv.runtime.capabilities)
+	config, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
@@ -92,7 +96,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e
 		return "", err
 		return "", err
 	}
 	}
 
 
-	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)\r", false), path); err != nil {
+	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), path); err != nil {
 		return "", err
 		return "", err
 	}
 	}
 	// FIXME: Handle custom repo, tag comment, author
 	// FIXME: Handle custom repo, tag comment, author
@@ -100,8 +104,8 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	fmt.Fprintf(out, "%s\n", img.Id)
-	return img.ShortId(), nil
+	out.Write(sf.FormatStatus(img.ID))
+	return img.ShortID(), nil
 }
 }
 
 
 func (srv *Server) ImagesViz(out io.Writer) error {
 func (srv *Server) ImagesViz(out io.Writer) error {
@@ -121,9 +125,9 @@ func (srv *Server) ImagesViz(out io.Writer) error {
 			return fmt.Errorf("Error while getting parent image: %v", err)
 			return fmt.Errorf("Error while getting parent image: %v", err)
 		}
 		}
 		if parentImage != nil {
 		if parentImage != nil {
-			out.Write([]byte(" \"" + parentImage.ShortId() + "\" -> \"" + image.ShortId() + "\"\n"))
+			out.Write([]byte(" \"" + parentImage.ShortID() + "\" -> \"" + image.ShortID() + "\"\n"))
 		} else {
 		} else {
-			out.Write([]byte(" base -> \"" + image.ShortId() + "\" [style=invis]\n"))
+			out.Write([]byte(" base -> \"" + image.ShortID() + "\" [style=invis]\n"))
 		}
 		}
 	}
 	}
 
 
@@ -131,7 +135,7 @@ func (srv *Server) ImagesViz(out io.Writer) error {
 
 
 	for name, repository := range srv.runtime.repositories.Repositories {
 	for name, repository := range srv.runtime.repositories.Repositories {
 		for tag, id := range repository {
 		for tag, id := range repository {
-			reporefs[utils.TruncateId(id)] = append(reporefs[utils.TruncateId(id)], fmt.Sprintf("%s:%s", name, tag))
+			reporefs[utils.TruncateID(id)] = append(reporefs[utils.TruncateID(id)], fmt.Sprintf("%s:%s", name, tag))
 		}
 		}
 	}
 	}
 
 
@@ -142,7 +146,7 @@ func (srv *Server) ImagesViz(out io.Writer) error {
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
+func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
 	var (
 	var (
 		allImages map[string]*Image
 		allImages map[string]*Image
 		err       error
 		err       error
@@ -155,13 +159,13 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	outs := []ApiImages{} //produce [] when empty instead of 'null'
+	outs := []APIImages{} //produce [] when empty instead of 'null'
 	for name, repository := range srv.runtime.repositories.Repositories {
 	for name, repository := range srv.runtime.repositories.Repositories {
 		if filter != "" && name != filter {
 		if filter != "" && name != filter {
 			continue
 			continue
 		}
 		}
 		for tag, id := range repository {
 		for tag, id := range repository {
-			var out ApiImages
+			var out APIImages
 			image, err := srv.runtime.graph.Get(id)
 			image, err := srv.runtime.graph.Get(id)
 			if err != nil {
 			if err != nil {
 				log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
 				log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
@@ -170,7 +174,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
 			delete(allImages, id)
 			delete(allImages, id)
 			out.Repository = name
 			out.Repository = name
 			out.Tag = tag
 			out.Tag = tag
-			out.Id = image.Id
+			out.ID = image.ID
 			out.Created = image.Created.Unix()
 			out.Created = image.Created.Unix()
 			out.Size = image.Size
 			out.Size = image.Size
 			out.ParentSize = image.getVirtualSize(0)
 			out.ParentSize = image.getVirtualSize(0)
@@ -180,8 +184,8 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
 	// Display images which aren't part of a
 	// Display images which aren't part of a
 	if filter == "" {
 	if filter == "" {
 		for _, image := range allImages {
 		for _, image := range allImages {
-			var out ApiImages
-			out.Id = image.Id
+			var out APIImages
+			out.ID = image.ID
 			out.Created = image.Created.Unix()
 			out.Created = image.Created.Unix()
 			out.Size = image.Size
 			out.Size = image.Size
 			out.ParentSize = image.getVirtualSize(0)
 			out.ParentSize = image.getVirtualSize(0)
@@ -191,7 +195,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
 	return outs, nil
 	return outs, nil
 }
 }
 
 
-func (srv *Server) DockerInfo() ApiInfo {
+func (srv *Server) DockerInfo() *APIInfo {
 	images, _ := srv.runtime.graph.All()
 	images, _ := srv.runtime.graph.All()
 	var imgcount int
 	var imgcount int
 	if images == nil {
 	if images == nil {
@@ -199,29 +203,27 @@ func (srv *Server) DockerInfo() ApiInfo {
 	} else {
 	} else {
 		imgcount = len(images)
 		imgcount = len(images)
 	}
 	}
-	var out ApiInfo
-	out.Containers = len(srv.runtime.List())
-	out.Version = VERSION
-	out.Images = imgcount
-	out.GoVersion = runtime.Version()
-	if os.Getenv("DEBUG") != "" {
-		out.Debug = true
-		out.NFd = utils.GetTotalUsedFds()
-		out.NGoroutines = runtime.NumGoroutine()
+	return &APIInfo{
+		Containers:  len(srv.runtime.List()),
+		Images:      imgcount,
+		MemoryLimit: srv.runtime.capabilities.MemoryLimit,
+		SwapLimit:   srv.runtime.capabilities.SwapLimit,
+		Debug:       os.Getenv("DEBUG") != "",
+		NFd:         utils.GetTotalUsedFds(),
+		NGoroutines: runtime.NumGoroutine(),
 	}
 	}
-	return out
 }
 }
 
 
-func (srv *Server) ImageHistory(name string) ([]ApiHistory, error) {
+func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
 	image, err := srv.runtime.repositories.LookupImage(name)
 	image, err := srv.runtime.repositories.LookupImage(name)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	var outs []ApiHistory = []ApiHistory{} //produce [] when empty instead of 'null'
+	outs := []APIHistory{} //produce [] when empty instead of 'null'
 	err = image.WalkHistory(func(img *Image) error {
 	err = image.WalkHistory(func(img *Image) error {
-		var out ApiHistory
-		out.Id = srv.runtime.repositories.ImageName(img.ShortId())
+		var out APIHistory
+		out.ID = srv.runtime.repositories.ImageName(img.ShortID())
 		out.Created = img.Created.Unix()
 		out.Created = img.Created.Unix()
 		out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
 		out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
 		outs = append(outs, out)
 		outs = append(outs, out)
@@ -238,17 +240,17 @@ func (srv *Server) ContainerChanges(name string) ([]Change, error) {
 	return nil, fmt.Errorf("No such container: %s", name)
 	return nil, fmt.Errorf("No such container: %s", name)
 }
 }
 
 
-func (srv *Server) Containers(all bool, n int, since, before string) []ApiContainers {
+func (srv *Server) Containers(all bool, n int, since, before string) []APIContainers {
 	var foundBefore bool
 	var foundBefore bool
 	var displayed int
 	var displayed int
-	retContainers := []ApiContainers{}
+	retContainers := []APIContainers{}
 
 
 	for _, container := range srv.runtime.List() {
 	for _, container := range srv.runtime.List() {
 		if !container.State.Running && !all && n == -1 && since == "" && before == "" {
 		if !container.State.Running && !all && n == -1 && since == "" && before == "" {
 			continue
 			continue
 		}
 		}
 		if before != "" {
 		if before != "" {
-			if container.ShortId() == before {
+			if container.ShortID() == before {
 				foundBefore = true
 				foundBefore = true
 				continue
 				continue
 			}
 			}
@@ -259,13 +261,13 @@ func (srv *Server) Containers(all bool, n int, since, before string) []ApiContai
 		if displayed == n {
 		if displayed == n {
 			break
 			break
 		}
 		}
-		if container.ShortId() == since {
+		if container.ShortID() == since {
 			break
 			break
 		}
 		}
 		displayed++
 		displayed++
 
 
-		c := ApiContainers{
-			Id: container.Id,
+		c := APIContainers{
+			ID: container.ID,
 		}
 		}
 		c.Image = srv.runtime.repositories.ImageName(container.Image)
 		c.Image = srv.runtime.repositories.ImageName(container.Image)
 		c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
 		c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
@@ -288,7 +290,7 @@ func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, conf
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	return img.ShortId(), err
+	return img.ShortID(), err
 }
 }
 
 
 func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
 func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
@@ -298,7 +300,7 @@ func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoint string, token []string, json bool) error {
+func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoint string, token []string, sf *utils.StreamFormatter) error {
 	history, err := r.GetRemoteHistory(imgId, endpoint, token)
 	history, err := r.GetRemoteHistory(imgId, endpoint, token)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -308,24 +310,25 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
 	// FIXME: Launch the getRemoteImage() in goroutines
 	// FIXME: Launch the getRemoteImage() in goroutines
 	for _, id := range history {
 	for _, id := range history {
 		if !srv.runtime.graph.Exists(id) {
 		if !srv.runtime.graph.Exists(id) {
-			fmt.Fprintf(out, utils.FormatStatus("Pulling %s metadata", json), id)
-			imgJson, err := r.GetRemoteImageJson(id, endpoint, token)
+			out.Write(sf.FormatStatus("Pulling %s metadata", id))
+			imgJSON, err := r.GetRemoteImageJSON(id, endpoint, token)
 			if err != nil {
 			if err != nil {
 				// FIXME: Keep goging in case of error?
 				// FIXME: Keep goging in case of error?
 				return err
 				return err
 			}
 			}
-			img, err := NewImgJson(imgJson)
+			img, err := NewImgJSON(imgJSON)
 			if err != nil {
 			if err != nil {
 				return fmt.Errorf("Failed to parse json: %s", err)
 				return fmt.Errorf("Failed to parse json: %s", err)
 			}
 			}
 
 
 			// Get the layer
 			// Get the layer
-			fmt.Fprintf(out, utils.FormatStatus("Pulling %s fs layer", json), id)
-			layer, contentLength, err := r.GetRemoteImageLayer(img.Id, endpoint, token)
+			out.Write(sf.FormatStatus("Pulling %s fs layer", id))
+			layer, contentLength, err := r.GetRemoteImageLayer(img.ID, endpoint, token)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
-			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, utils.FormatProgress("%v/%v (%v)", json), json), false, img); err != nil {
+			defer layer.Close()
+			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), false, img); err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}
@@ -333,8 +336,8 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, json bool) error {
-	fmt.Fprintf(out, utils.FormatStatus("Pulling repository %s from %s", json), remote, auth.IndexServerAddress())
+func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, sf *utils.StreamFormatter) error {
+	out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress()))
 	repoData, err := r.GetRepositoryData(remote)
 	repoData, err := r.GetRepositoryData(remote)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -359,23 +362,23 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
 		}
 		}
 	} else {
 	} else {
 		// Otherwise, check that the tag exists and use only that one
 		// Otherwise, check that the tag exists and use only that one
-		if id, exists := tagsList[askedTag]; !exists {
+		id, exists := tagsList[askedTag]
+		if !exists {
 			return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, remote)
 			return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, remote)
-		} else {
-			repoData.ImgList[id].Tag = askedTag
 		}
 		}
+		repoData.ImgList[id].Tag = askedTag
 	}
 	}
 
 
 	for _, img := range repoData.ImgList {
 	for _, img := range repoData.ImgList {
 		if askedTag != "" && img.Tag != askedTag {
 		if askedTag != "" && img.Tag != askedTag {
-			utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.Id)
+			utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.ID)
 			continue
 			continue
 		}
 		}
-		fmt.Fprintf(out, utils.FormatStatus("Pulling image %s (%s) from %s", json), img.Id, img.Tag, remote)
+		out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", img.ID, img.Tag, remote))
 		success := false
 		success := false
 		for _, ep := range repoData.Endpoints {
 		for _, ep := range repoData.Endpoints {
-			if err := srv.pullImage(r, out, img.Id, "https://"+ep+"/v1", repoData.Tokens, json); err != nil {
-				fmt.Fprintf(out, utils.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint\n", json), askedTag, err)
+			if err := srv.pullImage(r, out, img.ID, "https://"+ep+"/v1", repoData.Tokens, sf); err != nil {
+				out.Write(sf.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint", askedTag, err))
 				continue
 				continue
 			}
 			}
 			success = true
 			success = true
@@ -400,17 +403,17 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, json bool) error {
+func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
 	r := registry.NewRegistry(srv.runtime.root)
 	r := registry.NewRegistry(srv.runtime.root)
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
 	if endpoint != "" {
 	if endpoint != "" {
-		if err := srv.pullImage(r, out, name, endpoint, nil, json); err != nil {
+		if err := srv.pullImage(r, out, name, endpoint, nil, sf); err != nil {
 			return err
 			return err
 		}
 		}
 		return nil
 		return nil
 	}
 	}
 
 
-	if err := srv.pullRepository(r, out, name, tag, json); err != nil {
+	if err := srv.pullRepository(r, out, name, tag, sf); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -464,16 +467,16 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat
 			return nil, err
 			return nil, err
 		}
 		}
 		img.WalkHistory(func(img *Image) error {
 		img.WalkHistory(func(img *Image) error {
-			if _, exists := imageSet[img.Id]; exists {
+			if _, exists := imageSet[img.ID]; exists {
 				return nil
 				return nil
 			}
 			}
-			imageSet[img.Id] = struct{}{}
-			checksum, err := srv.getChecksum(img.Id)
+			imageSet[img.ID] = struct{}{}
+			checksum, err := srv.getChecksum(img.ID)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
 			imgList = append([]*registry.ImgData{{
 			imgList = append([]*registry.ImgData{{
-				Id:       img.Id,
+				ID:       img.ID,
 				Checksum: checksum,
 				Checksum: checksum,
 				Tag:      tag,
 				Tag:      tag,
 			}}, imgList...)
 			}}, imgList...)
@@ -483,52 +486,52 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat
 	return imgList, nil
 	return imgList, nil
 }
 }
 
 
-func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name string, localRepo map[string]string) error {
+func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name string, localRepo map[string]string, sf *utils.StreamFormatter) error {
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
-	fmt.Fprintf(out, "Processing checksums\n")
+	out.Write(sf.FormatStatus("Processing checksums"))
 	imgList, err := srv.getImageList(localRepo)
 	imgList, err := srv.getImageList(localRepo)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	fmt.Fprintf(out, "Sending images list\n")
+	out.Write(sf.FormatStatus("Sending image list"))
 
 
-	repoData, err := r.PushImageJsonIndex(name, imgList, false)
+	repoData, err := r.PushImageJSONIndex(name, imgList, false)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	for _, ep := range repoData.Endpoints {
 	for _, ep := range repoData.Endpoints {
-		fmt.Fprintf(out, "Pushing repository %s to %s (%d tags)\r\n", name, ep, len(localRepo))
+		out.Write(sf.FormatStatus("Pushing repository %s to %s (%d tags)", name, ep, len(localRepo)))
 		// For each image within the repo, push them
 		// For each image within the repo, push them
 		for _, elem := range imgList {
 		for _, elem := range imgList {
-			if _, exists := repoData.ImgList[elem.Id]; exists {
-				fmt.Fprintf(out, "Image %s already on registry, skipping\n", name)
+			if _, exists := repoData.ImgList[elem.ID]; exists {
+				out.Write(sf.FormatStatus("Image %s already on registry, skipping", name))
 				continue
 				continue
 			}
 			}
-			if err := srv.pushImage(r, out, name, elem.Id, ep, repoData.Tokens); err != nil {
+			if err := srv.pushImage(r, out, name, elem.ID, ep, repoData.Tokens, sf); err != nil {
 				// FIXME: Continue on error?
 				// FIXME: Continue on error?
 				return err
 				return err
 			}
 			}
-			fmt.Fprintf(out, "Pushing tags for rev [%s] on {%s}\n", elem.Id, ep+"/users/"+name+"/"+elem.Tag)
-			if err := r.PushRegistryTag(name, elem.Id, elem.Tag, ep, repoData.Tokens); err != nil {
+			out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+name+"/"+elem.Tag))
+			if err := r.PushRegistryTag(name, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	if _, err := r.PushImageJsonIndex(name, imgList, true); err != nil {
+	if _, err := r.PushImageJSONIndex(name, imgList, true); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, ep string, token []string) error {
+func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, ep string, token []string, sf *utils.StreamFormatter) error {
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
 	jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgId, "json"))
 	jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgId, "json"))
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("Error while retreiving the path for {%s}: %s", imgId, err)
 		return fmt.Errorf("Error while retreiving the path for {%s}: %s", imgId, err)
 	}
 	}
-	fmt.Fprintf(out, "Pushing %s\r\n", imgId)
+	out.Write(sf.FormatStatus("Pushing %s", imgId))
 
 
 	// Make sure we have the image's checksum
 	// Make sure we have the image's checksum
 	checksum, err := srv.getChecksum(imgId)
 	checksum, err := srv.getChecksum(imgId)
@@ -536,14 +539,14 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
 		return err
 		return err
 	}
 	}
 	imgData := &registry.ImgData{
 	imgData := &registry.ImgData{
-		Id:       imgId,
+		ID:       imgId,
 		Checksum: checksum,
 		Checksum: checksum,
 	}
 	}
 
 
 	// Send the json
 	// Send the json
-	if err := r.PushImageJsonRegistry(imgData, jsonRaw, ep, token); err != nil {
+	if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep, token); err != nil {
 		if err == registry.ErrAlreadyExists {
 		if err == registry.ErrAlreadyExists {
-			fmt.Fprintf(out, "Image %s already uploaded ; skipping\n", imgData.Id)
+			out.Write(sf.FormatStatus("Image %s already uploaded ; skipping", imgData.ID))
 			return nil
 			return nil
 		}
 		}
 		return err
 		return err
@@ -576,22 +579,22 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
 	}
 	}
 
 
 	// Send the layer
 	// Send the layer
-	if err := r.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, "", false), ep, token); err != nil {
+	if err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("Pushing", "%v/%v (%v)"), sf), ep, token); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) ImagePush(name, endpoint string, out io.Writer) error {
+func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
 	img, err := srv.runtime.graph.Get(name)
 	img, err := srv.runtime.graph.Get(name)
 	r := registry.NewRegistry(srv.runtime.root)
 	r := registry.NewRegistry(srv.runtime.root)
 
 
 	if err != nil {
 	if err != nil {
-		fmt.Fprintf(out, "The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name]))
+		out.Write(sf.FormatStatus("The push refers to a repository [%s] (len: %d)", name, len(srv.runtime.repositories.Repositories[name])))
 		// If it fails, try to get the repository
 		// If it fails, try to get the repository
 		if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists {
 		if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists {
-			if err := srv.pushRepository(r, out, name, localRepo); err != nil {
+			if err := srv.pushRepository(r, out, name, localRepo, sf); err != nil {
 				return err
 				return err
 			}
 			}
 			return nil
 			return nil
@@ -599,14 +602,14 @@ func (srv *Server) ImagePush(name, endpoint string, out io.Writer) error {
 
 
 		return err
 		return err
 	}
 	}
-	fmt.Fprintf(out, "The push refers to an image: [%s]\n", name)
-	if err := srv.pushImage(r, out, name, img.Id, endpoint, nil); err != nil {
+	out.Write(sf.FormatStatus("The push refers to an image: [%s]", name))
+	if err := srv.pushImage(r, out, name, img.ID, endpoint, nil, sf); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Writer) error {
+func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Writer, sf *utils.StreamFormatter) error {
 	var archive io.Reader
 	var archive io.Reader
 	var resp *http.Response
 	var resp *http.Response
 
 
@@ -615,21 +618,21 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
 	} else {
 	} else {
 		u, err := url.Parse(src)
 		u, err := url.Parse(src)
 		if err != nil {
 		if err != nil {
-			fmt.Fprintf(out, "Error: %s\n", err)
+			return err
 		}
 		}
 		if u.Scheme == "" {
 		if u.Scheme == "" {
 			u.Scheme = "http"
 			u.Scheme = "http"
 			u.Host = src
 			u.Host = src
 			u.Path = ""
 			u.Path = ""
 		}
 		}
-		fmt.Fprintf(out, "Downloading from %s\n", u)
+		out.Write(sf.FormatStatus("Downloading from %s", u))
 		// Download with curl (pretty progress bar)
 		// Download with curl (pretty progress bar)
 		// If curl is not available, fallback to http.Get()
 		// If curl is not available, fallback to http.Get()
 		resp, err = utils.Download(u.String(), out)
 		resp, err = utils.Download(u.String(), out)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)\r", false)
+		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("Importing", "%v/%v (%v)"), sf)
 	}
 	}
 	img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
 	img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
 	if err != nil {
 	if err != nil {
@@ -637,11 +640,11 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
 	}
 	}
 	// Optionally register the image at REPO/TAG
 	// Optionally register the image at REPO/TAG
 	if repo != "" {
 	if repo != "" {
-		if err := srv.runtime.repositories.Set(repo, tag, img.Id, true); err != nil {
+		if err := srv.runtime.repositories.Set(repo, tag, img.ID, true); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	fmt.Fprintf(out, "%s\n", img.ShortId())
+	out.Write(sf.FormatStatus(img.ShortID()))
 	return nil
 	return nil
 }
 }
 
 
@@ -662,7 +665,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
 		}
 		}
 		return "", err
 		return "", err
 	}
 	}
-	return container.ShortId(), nil
+	return container.ShortID(), nil
 }
 }
 
 
 func (srv *Server) ContainerRestart(name string, t int) error {
 func (srv *Server) ContainerRestart(name string, t int) error {
@@ -699,7 +702,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
 			for volumeId := range volumes {
 			for volumeId := range volumes {
 				// If the requested volu
 				// If the requested volu
 				if c, exists := usedVolumes[volumeId]; exists {
 				if c, exists := usedVolumes[volumeId]; exists {
-					log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.Id)
+					log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.ID)
 					continue
 					continue
 				}
 				}
 				if err := srv.runtime.volumes.Delete(volumeId); err != nil {
 				if err := srv.runtime.volumes.Delete(volumeId); err != nil {
@@ -717,10 +720,9 @@ func (srv *Server) ImageDelete(name string) error {
 	img, err := srv.runtime.repositories.LookupImage(name)
 	img, err := srv.runtime.repositories.LookupImage(name)
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("No such image: %s", name)
 		return fmt.Errorf("No such image: %s", name)
-	} else {
-		if err := srv.runtime.graph.Delete(img.Id); err != nil {
-			return fmt.Errorf("Error deleting image %s: %s", name, err.Error())
-		}
+	}
+	if err := srv.runtime.graph.Delete(img.ID); err != nil {
+		return fmt.Errorf("Error deleting image %s: %s", name, err.Error())
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -739,7 +741,7 @@ func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error)
 		if _, exists := imageMap[img.Parent]; !exists {
 		if _, exists := imageMap[img.Parent]; !exists {
 			imageMap[img.Parent] = make(map[string]struct{})
 			imageMap[img.Parent] = make(map[string]struct{})
 		}
 		}
-		imageMap[img.Parent][img.Id] = struct{}{}
+		imageMap[img.Parent][img.ID] = struct{}{}
 	}
 	}
 
 
 	// Loop on the children of the given image and check the config
 	// Loop on the children of the given image and check the config
@@ -796,7 +798,6 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
 	if container == nil {
 	if container == nil {
 		return fmt.Errorf("No such container: %s", name)
 		return fmt.Errorf("No such container: %s", name)
 	}
 	}
-
 	//logs
 	//logs
 	if logs {
 	if logs {
 		if stdout {
 		if stdout {
@@ -822,6 +823,9 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
 		if container.State.Ghost {
 		if container.State.Ghost {
 			return fmt.Errorf("Impossible to attach to a ghost container")
 			return fmt.Errorf("Impossible to attach to a ghost container")
 		}
 		}
+		if !container.State.Running {
+			return fmt.Errorf("Impossible to attach to a stopped container, start it first")
+		}
 
 
 		var (
 		var (
 			cStdin           io.ReadCloser
 			cStdin           io.ReadCloser

+ 2 - 2
server_test.go

@@ -13,7 +13,7 @@ func TestCreateRm(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	config, _, err := ParseRun([]string{GetTestImage(runtime).Id, "echo test"}, nil)
+	config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -46,7 +46,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
 
 
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
-	config, _, err := ParseRun([]string{GetTestImage(runtime).Id, "/bin/cat"}, nil)
+	config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "/bin/cat"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 13 - 13
tags.go

@@ -11,7 +11,7 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-const DEFAULT_TAG = "latest"
+const DEFAULTTAG = "latest"
 
 
 type TagStore struct {
 type TagStore struct {
 	path         string
 	path         string
@@ -72,7 +72,7 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
 		// (so we can pass all errors here)
 		// (so we can pass all errors here)
 		repoAndTag := strings.SplitN(name, ":", 2)
 		repoAndTag := strings.SplitN(name, ":", 2)
 		if len(repoAndTag) == 1 {
 		if len(repoAndTag) == 1 {
-			repoAndTag = append(repoAndTag, DEFAULT_TAG)
+			repoAndTag = append(repoAndTag, DEFAULTTAG)
 		}
 		}
 		if i, err := store.GetImage(repoAndTag[0], repoAndTag[1]); err != nil {
 		if i, err := store.GetImage(repoAndTag[0], repoAndTag[1]); err != nil {
 			return nil, err
 			return nil, err
@@ -87,27 +87,27 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
 
 
 // Return a reverse-lookup table of all the names which refer to each image
 // Return a reverse-lookup table of all the names which refer to each image
 // Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
 // Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
-func (store *TagStore) ById() map[string][]string {
-	byId := make(map[string][]string)
+func (store *TagStore) ByID() map[string][]string {
+	byID := make(map[string][]string)
 	for repoName, repository := range store.Repositories {
 	for repoName, repository := range store.Repositories {
 		for tag, id := range repository {
 		for tag, id := range repository {
 			name := repoName + ":" + tag
 			name := repoName + ":" + tag
-			if _, exists := byId[id]; !exists {
-				byId[id] = []string{name}
+			if _, exists := byID[id]; !exists {
+				byID[id] = []string{name}
 			} else {
 			} else {
-				byId[id] = append(byId[id], name)
-				sort.Strings(byId[id])
+				byID[id] = append(byID[id], name)
+				sort.Strings(byID[id])
 			}
 			}
 		}
 		}
 	}
 	}
-	return byId
+	return byID
 }
 }
 
 
 func (store *TagStore) ImageName(id string) string {
 func (store *TagStore) ImageName(id string) string {
-	if names, exists := store.ById()[id]; exists && len(names) > 0 {
+	if names, exists := store.ByID()[id]; exists && len(names) > 0 {
 		return names[0]
 		return names[0]
 	}
 	}
-	return utils.TruncateId(id)
+	return utils.TruncateID(id)
 }
 }
 
 
 func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
 func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
@@ -116,7 +116,7 @@ func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
 		return err
 		return err
 	}
 	}
 	if tag == "" {
 	if tag == "" {
-		tag = DEFAULT_TAG
+		tag = DEFAULTTAG
 	}
 	}
 	if err := validateRepoName(repoName); err != nil {
 	if err := validateRepoName(repoName); err != nil {
 		return err
 		return err
@@ -137,7 +137,7 @@ func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
 		}
 		}
 		store.Repositories[repoName] = repo
 		store.Repositories[repoName] = repo
 	}
 	}
-	repo[tag] = img.Id
+	repo[tag] = img.ID
 	return store.Save()
 	return store.Save()
 }
 }
 
 

+ 2 - 0
term/MAINTAINERS

@@ -0,0 +1,2 @@
+Guillaume Charmes <guillaume@dotcloud.com>
+Solomon Hykes <solomon@dotcloud.com>

+ 7 - 105
term/term.go

@@ -7,104 +7,6 @@ import (
 	"unsafe"
 	"unsafe"
 )
 )
 
 
-type Termios struct {
-	Iflag  uintptr
-	Oflag  uintptr
-	Cflag  uintptr
-	Lflag  uintptr
-	Cc     [20]byte
-	Ispeed uintptr
-	Ospeed uintptr
-}
-
-const (
-	// Input flags
-	inpck  = 0x010
-	istrip = 0x020
-	icrnl  = 0x100
-	ixon   = 0x200
-
-	// Output flags
-	opost = 0x1
-
-	// Control flags
-	cs8 = 0x300
-
-	// Local flags
-	icanon = 0x100
-	iexten = 0x400
-)
-
-const (
-	HUPCL   = 0x4000
-	ICANON  = 0x100
-	ICRNL   = 0x100
-	IEXTEN  = 0x400
-	BRKINT  = 0x2
-	CFLUSH  = 0xf
-	CLOCAL  = 0x8000
-	CREAD   = 0x800
-	CS5     = 0x0
-	CS6     = 0x100
-	CS7     = 0x200
-	CS8     = 0x300
-	CSIZE   = 0x300
-	CSTART  = 0x11
-	CSTATUS = 0x14
-	CSTOP   = 0x13
-	CSTOPB  = 0x400
-	CSUSP   = 0x1a
-	IGNBRK  = 0x1
-	IGNCR   = 0x80
-	IGNPAR  = 0x4
-	IMAXBEL = 0x2000
-	INLCR   = 0x40
-	INPCK   = 0x10
-	ISIG    = 0x80
-	ISTRIP  = 0x20
-	IUTF8   = 0x4000
-	IXANY   = 0x800
-	IXOFF   = 0x400
-	IXON    = 0x200
-	NOFLSH  = 0x80000000
-	OCRNL   = 0x10
-	OFDEL   = 0x20000
-	OFILL   = 0x80
-	ONLCR   = 0x2
-	ONLRET  = 0x40
-	ONOCR   = 0x20
-	ONOEOT  = 0x8
-	OPOST   = 0x1
-	RENB    = 0x1000
-	PARMRK  = 0x8
-	PARODD  = 0x2000
-
-	TOSTOP   = 0x400000
-	VDISCARD = 0xf
-	VDSUSP   = 0xb
-	VEOF     = 0x0
-	VEOL     = 0x1
-	VEOL2    = 0x2
-	VERASE   = 0x3
-	VINTR    = 0x8
-	VKILL    = 0x5
-	VLNEXT   = 0xe
-	VMIN     = 0x10
-	VQUIT    = 0x9
-	VREPRINT = 0x6
-	VSTART   = 0xc
-	VSTATUS  = 0x12
-	VSTOP    = 0xd
-	VSUSP    = 0xa
-	VT0      = 0x0
-	VT1      = 0x10000
-	VTDLY    = 0x10000
-	VTIME    = 0x11
-	ECHO     = 0x00000008
-
-	PENDIN = 0x20000000
-)
-
 type State struct {
 type State struct {
 	termios Termios
 	termios Termios
 }
 }
@@ -128,21 +30,21 @@ func SetWinsize(fd uintptr, ws *Winsize) error {
 }
 }
 
 
 // IsTerminal returns true if the given file descriptor is a terminal.
 // IsTerminal returns true if the given file descriptor is a terminal.
-func IsTerminal(fd int) bool {
+func IsTerminal(fd uintptr) bool {
 	var termios Termios
 	var termios Termios
-	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
 	return err == 0
 	return err == 0
 }
 }
 
 
 // Restore restores the terminal connected to the given file descriptor to a
 // Restore restores the terminal connected to the given file descriptor to a
 // previous state.
 // previous state.
-func Restore(fd int, state *State) error {
-	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
+func Restore(fd uintptr, state *State) error {
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
 	return err
 	return err
 }
 }
 
 
 func SetRawTerminal() (*State, error) {
 func SetRawTerminal() (*State, error) {
-	oldState, err := MakeRaw(int(os.Stdin.Fd()))
+	oldState, err := MakeRaw(os.Stdin.Fd())
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -150,12 +52,12 @@ func SetRawTerminal() (*State, error) {
 	signal.Notify(c, os.Interrupt)
 	signal.Notify(c, os.Interrupt)
 	go func() {
 	go func() {
 		_ = <-c
 		_ = <-c
-		Restore(int(os.Stdin.Fd()), oldState)
+		Restore(os.Stdin.Fd(), oldState)
 		os.Exit(0)
 		os.Exit(0)
 	}()
 	}()
 	return oldState, err
 	return oldState, err
 }
 }
 
 
 func RestoreTerminal(state *State) {
 func RestoreTerminal(state *State) {
-	Restore(int(os.Stdin.Fd()), state)
+	Restore(os.Stdin.Fd(), state)
 }
 }

+ 27 - 5
term/termios_darwin.go

@@ -8,23 +8,45 @@ import (
 const (
 const (
 	getTermios = syscall.TIOCGETA
 	getTermios = syscall.TIOCGETA
 	setTermios = syscall.TIOCSETA
 	setTermios = syscall.TIOCSETA
+
+	ECHO    = 0x00000008
+	ONLCR   = 0x2
+	ISTRIP  = 0x20
+	INLCR   = 0x40
+	ISIG    = 0x80
+	IGNCR   = 0x80
+	ICANON  = 0x100
+	ICRNL   = 0x100
+	IXOFF   = 0x400
+	IXON    = 0x200
 )
 )
 
 
+type Termios struct {
+	Iflag  uint64
+	Oflag  uint64
+	Cflag  uint64
+	Lflag  uint64
+	Cc     [20]byte
+	Ispeed uint64
+	Ospeed uint64
+}
+
 // MakeRaw put the terminal connected to the given file descriptor into raw
 // MakeRaw put the terminal connected to the given file descriptor into raw
 // mode and returns the previous state of the terminal so that it can be
 // mode and returns the previous state of the terminal so that it can be
 // restored.
 // restored.
-func MakeRaw(fd int) (*State, error) {
+func MakeRaw(fd uintptr) (*State, error) {
 	var oldState State
 	var oldState State
-	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	newState := oldState.termios
 	newState := oldState.termios
-	newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF
+	newState.Iflag &^= (ISTRIP | INLCR | IGNCR | IXON | IXOFF)
 	newState.Iflag |= ICRNL
 	newState.Iflag |= ICRNL
 	newState.Oflag |= ONLCR
 	newState.Oflag |= ONLCR
-	newState.Lflag &^= ECHO | ICANON | ISIG
-	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
+	newState.Lflag &^= (ECHO | ICANON | ISIG)
+
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
 		return nil, err
 		return nil, err
 	}
 	}
 
 

+ 22 - 36
term/termios_linux.go

@@ -5,54 +5,40 @@ import (
 	"unsafe"
 	"unsafe"
 )
 )
 
 
-// #include <termios.h>
-// #include <sys/ioctl.h>
-/*
-void MakeRaw(int fd) {
-  struct termios t;
-
-  // FIXME: Handle errors?
-  ioctl(fd, TCGETS, &t);
-
-  t.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
-  t.c_oflag &= ~OPOST;
-  t.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
-  t.c_cflag &= ~(CSIZE | PARENB);
-  t.c_cflag |= CS8;
-
-  ioctl(fd, TCSETS, &t);
-}
-*/
-import "C"
-
 const (
 const (
 	getTermios = syscall.TCGETS
 	getTermios = syscall.TCGETS
 	setTermios = syscall.TCSETS
 	setTermios = syscall.TCSETS
 )
 )
 
 
+type Termios struct {
+	Iflag  uint32
+	Oflag  uint32
+	Cflag  uint32
+	Lflag  uint32
+	Cc     [20]byte
+	Ispeed uint32
+	Ospeed uint32
+}
+
 // MakeRaw put the terminal connected to the given file descriptor into raw
 // MakeRaw put the terminal connected to the given file descriptor into raw
 // mode and returns the previous state of the terminal so that it can be
 // mode and returns the previous state of the terminal so that it can be
 // restored.
 // restored.
-func MakeRaw(fd int) (*State, error) {
+func MakeRaw(fd uintptr) (*State, error) {
 	var oldState State
 	var oldState State
-	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TCGETS, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
 		return nil, err
 		return nil, err
 	}
 	}
-	C.MakeRaw(C.int(fd))
-	return &oldState, nil
-
-	// FIXME: post on goland issues this: very same as the C function bug non-working
 
 
-	// newState := oldState.termios
+	newState := oldState.termios
 
 
-	// newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
-	// newState.Oflag &^= OPOST
-	// newState.Lflag &^= (ECHO | syscall.ECHONL | ICANON | ISIG | IEXTEN)
-	// newState.Cflag &^= (CSIZE | syscall.PARENB)
-	// newState.Cflag |= CS8
+	newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON)
+	newState.Oflag &^= syscall.OPOST
+	newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
+	newState.Cflag &^= (syscall.CSIZE | syscall.PARENB)
+	newState.Cflag |= syscall.CS8
 
 
-	// if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TCSETS, uintptr(unsafe.Pointer(&newState))); err != 0 {
-	// 	return nil, err
-	// }
-	// return &oldState, nil
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
+		return nil, err
+	}
+	return &oldState, nil
 }
 }

+ 60 - 17
utils/utils.go

@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"bytes"
 	"crypto/sha256"
 	"crypto/sha256"
 	"encoding/hex"
 	"encoding/hex"
+	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"index/suffixarray"
 	"index/suffixarray"
@@ -33,7 +34,7 @@ func Go(f func() error) chan error {
 // Request a given URL and return an io.Reader
 // Request a given URL and return an io.Reader
 func Download(url string, stderr io.Writer) (*http.Response, error) {
 func Download(url string, stderr io.Writer) (*http.Response, error) {
 	var resp *http.Response
 	var resp *http.Response
-	var err error = nil
+	var err error
 	if resp, err = http.Get(url); err != nil {
 	if resp, err = http.Get(url); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -69,7 +70,7 @@ type progressReader struct {
 	readProgress int           // How much has been read so far (bytes)
 	readProgress int           // How much has been read so far (bytes)
 	lastUpdate   int           // How many bytes read at least update
 	lastUpdate   int           // How many bytes read at least update
 	template     string        // Template to print. Default "%v/%v (%v)"
 	template     string        // Template to print. Default "%v/%v (%v)"
-	json         bool
+	sf *StreamFormatter
 }
 }
 
 
 func (r *progressReader) Read(p []byte) (n int, err error) {
 func (r *progressReader) Read(p []byte) (n int, err error) {
@@ -93,7 +94,7 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
 	}
 	}
 	// Send newline when complete
 	// Send newline when complete
 	if err != nil {
 	if err != nil {
-		fmt.Fprintf(r.output, FormatStatus("", r.json))
+		r.output.Write(r.sf.FormatStatus(""))
 	}
 	}
 
 
 	return read, err
 	return read, err
@@ -101,11 +102,12 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
 func (r *progressReader) Close() error {
 func (r *progressReader) Close() error {
 	return io.ReadCloser(r.reader).Close()
 	return io.ReadCloser(r.reader).Close()
 }
 }
-func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string, json bool) *progressReader {
-	if template == "" {
-		template = "%v/%v (%v)\r"
+func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte, sf *StreamFormatter) *progressReader {
+      	tpl := string(template)
+	if tpl == "" {
+		tpl = string(sf.FormatProgress("", "%v/%v (%v)"))
 	}
 	}
-	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template, json}
+	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, tpl, sf}
 }
 }
 
 
 // HumanDuration returns a human-readable approximation of a duration
 // HumanDuration returns a human-readable approximation of a duration
@@ -361,11 +363,11 @@ func (idx *TruncIndex) Get(s string) (string, error) {
 	return string(idx.bytes[before:after]), err
 	return string(idx.bytes[before:after]), err
 }
 }
 
 
-// TruncateId returns a shorthand version of a string identifier for convenience.
+// TruncateID returns a shorthand version of a string identifier for convenience.
 // A collision with other shorthands is very unlikely, but possible.
 // A collision with other shorthands is very unlikely, but possible.
 // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
 // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
 // will need to use a langer prefix, or the full-length Id.
 // will need to use a langer prefix, or the full-length Id.
-func TruncateId(id string) string {
+func TruncateID(id string) string {
 	shortLen := 12
 	shortLen := 12
 	if len(id) < shortLen {
 	if len(id) < shortLen {
 		shortLen = len(id)
 		shortLen = len(id)
@@ -578,16 +580,57 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
 	return &WriteFlusher{w: w, flusher: flusher}
 	return &WriteFlusher{w: w, flusher: flusher}
 }
 }
 
 
-func FormatStatus(str string, json bool) string {
-	if json {
-		return "{\"status\" : \"" + str + "\"}"
+type JSONMessage struct {
+	Status   string `json:"status,omitempty"`
+	Progress string `json:"progress,omitempty"`
+	Error    string `json:"error,omitempty"`
+}
+
+type StreamFormatter struct {
+	json bool
+	used bool
+}
+
+func NewStreamFormatter(json bool) *StreamFormatter {
+	return &StreamFormatter{json, false}
+}
+
+func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte {
+	sf.used = true
+	str := fmt.Sprintf(format, a...)
+	if sf.json {
+		b, err := json.Marshal(&JSONMessage{Status:str});
+		if err != nil {
+			return sf.FormatError(err)
+		}
+		return b
 	}
 	}
-	return str + "\r\n"
+	return []byte(str + "\r\n")
 }
 }
 
 
-func FormatProgress(str string, json bool) string {
-	if json {
-		return "{\"progress\" : \"" + str + "\"}"
+func (sf *StreamFormatter) FormatError(err error) []byte {
+	sf.used = true
+	if sf.json {
+		if b, err := json.Marshal(&JSONMessage{Error:err.Error()}); err == nil {
+			return b
+		}
+		return []byte("{\"error\":\"format error\"}")
 	}
 	}
-	return "Downloading " + str + "\r"
+	return []byte("Error: " + err.Error() + "\r\n")
+}
+
+func (sf *StreamFormatter) FormatProgress(action, str string) []byte {
+	sf.used = true
+	if sf.json {
+		b, err := json.Marshal(&JSONMessage{Status: action, Progress:str})
+		if err != nil {
+                        return nil
+                }
+		return b
+	}
+	return []byte(action + " " + str + "\r")
+}
+
+func (sf *StreamFormatter) Used() bool {
+	return sf.used
 }
 }