Bläddra i källkod

first version of Pull

Victor Vieux 12 år sedan
förälder
incheckning
3c7bca7a21
14 ändrade filer med 251 tillägg och 214 borttagningar
  1. 4 0
      CHANGELOG.md
  2. 52 38
      api.go
  3. 26 26
      api_test.go
  4. 0 6
      builder_client.go
  5. 7 9
      commands.go
  6. 1 1
      docs/sources/commandline/command/run.rst
  7. 43 81
      docs/sources/installation/kernel.rst
  8. 29 0
      docs/website/index.html
  9. 1 1
      graph.go
  10. 35 28
      packaging/ubuntu/changelog
  11. 6 1
      registry/registry.go
  12. 17 15
      server.go
  13. 2 2
      utils.go
  14. 28 6
      utils/utils.go

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 # Changelog
 # Changelog
 
 
+## 0.3.3 (2013-05-23)
+ - Registry: Fix push regression
+ - Various bugfixes
+
 ## 0.3.2 (2013-05-09)
 ## 0.3.2 (2013-05-09)
  * Runtime: Store the actual archive on commit
  * Runtime: Store the actual archive on commit
  * Registry: Improve the checksum process
  * Registry: Improve the checksum process

+ 52 - 38
api.go

@@ -13,6 +13,8 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
+const API_VERSION = 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()
 	if err != nil {
 	if err != nil {
@@ -56,8 +58,8 @@ func getBoolParam(value string) (bool, error) {
 	return false, fmt.Errorf("Bad parameter")
 	return false, fmt.Errorf("Bad parameter")
 }
 }
 
 
-func getAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
-	b, err := json.Marshal(srv.registry.GetAuthConfig())
+func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	b, err := json.Marshal(srv.registry.GetAuthConfig(false))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -65,14 +67,14 @@ func getAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[strin
 	return nil
 	return nil
 }
 }
 
 
-func postAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	config := &auth.AuthConfig{}
 	config := &auth.AuthConfig{}
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
 		return err
 		return err
 	}
 	}
-
-	if config.Username == srv.registry.GetAuthConfig().Username {
-		config.Password = srv.registry.GetAuthConfig().Password
+	authConfig := srv.registry.GetAuthConfig(true)
+	if config.Username == authConfig.Username {
+		config.Password = authConfig.Password
 	}
 	}
 
 
 	newAuthConfig := auth.NewAuthConfig(config.Username, config.Password, config.Email, srv.runtime.root)
 	newAuthConfig := auth.NewAuthConfig(config.Username, config.Password, config.Email, srv.runtime.root)
@@ -94,7 +96,7 @@ func postAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[stri
 	return nil
 	return nil
 }
 }
 
 
-func getVersion(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	m := srv.DockerVersion()
 	m := srv.DockerVersion()
 	b, err := json.Marshal(m)
 	b, err := json.Marshal(m)
 	if err != nil {
 	if err != nil {
@@ -104,7 +106,7 @@ func getVersion(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st
 	return nil
 	return nil
 }
 }
 
 
-func postContainersKill(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
@@ -116,7 +118,7 @@ func postContainersKill(srv *Server, w http.ResponseWriter, r *http.Request, var
 	return nil
 	return nil
 }
 }
 
 
-func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
@@ -129,7 +131,7 @@ func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, va
 	return nil
 	return nil
 }
 }
 
 
-func getImagesJson(srv *Server, 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
 	}
 	}
@@ -152,14 +154,14 @@ func getImagesJson(srv *Server, w http.ResponseWriter, r *http.Request, vars map
 	return nil
 	return nil
 }
 }
 
 
-func getImagesViz(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := srv.ImagesViz(w); err != nil {
 	if err := srv.ImagesViz(w); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func getInfo(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	out := srv.DockerInfo()
 	out := srv.DockerInfo()
 	b, err := json.Marshal(out)
 	b, err := json.Marshal(out)
 	if err != nil {
 	if err != nil {
@@ -169,7 +171,7 @@ func getInfo(srv *Server, w http.ResponseWriter, r *http.Request, vars map[strin
 	return nil
 	return nil
 }
 }
 
 
-func getImagesHistory(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
@@ -186,7 +188,7 @@ func getImagesHistory(srv *Server, w http.ResponseWriter, r *http.Request, vars
 	return nil
 	return nil
 }
 }
 
 
-func getContainersChanges(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
@@ -203,7 +205,7 @@ func getContainersChanges(srv *Server, w http.ResponseWriter, r *http.Request, v
 	return nil
 	return nil
 }
 }
 
 
-func getContainersPs(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getContainersPs(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
 	}
 	}
@@ -227,7 +229,7 @@ func getContainersPs(srv *Server, w http.ResponseWriter, r *http.Request, vars m
 	return nil
 	return nil
 }
 }
 
 
-func postImagesTag(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 	if err := parseForm(r); err != nil {
 		return err
 		return err
 	}
 	}
@@ -249,7 +251,7 @@ func postImagesTag(srv *Server, w http.ResponseWriter, r *http.Request, vars map
 	return nil
 	return nil
 }
 }
 
 
-func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postCommit(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
 	}
 	}
@@ -276,7 +278,7 @@ func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st
 }
 }
 
 
 // Creates an image from Pull or from Import
 // Creates an image from Pull or from Import
-func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postImagesCreate(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
 	}
 	}
@@ -288,8 +290,10 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars
 
 
 	if image != "" { //pull
 	if image != "" { //pull
 		registry := r.Form.Get("registry")
 		registry := r.Form.Get("registry")
-		w.Header().Set("Content-Type", "application/json")
-		if err := srv.ImagePull(image, tag, registry, w); err != nil {
+		if version > 1.0 {
+			w.Header().Set("Content-Type", "application/json")
+		}
+		if err := srv.ImagePull(image, tag, registry, w, version > 1.0); err != nil {
 			return err
 			return err
 		}
 		}
 	} else { //import
 	} else { //import
@@ -300,7 +304,7 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars
 	return nil
 	return nil
 }
 }
 
 
-func getImagesSearch(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getImagesSearch(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
 	}
 	}
@@ -318,7 +322,7 @@ func getImagesSearch(srv *Server, w http.ResponseWriter, r *http.Request, vars m
 	return nil
 	return nil
 }
 }
 
 
-func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 	if err := parseForm(r); err != nil {
 		return err
 		return err
 	}
 	}
@@ -336,7 +340,7 @@ func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars
 	return nil
 	return nil
 }
 }
 
 
-func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 	if err := parseForm(r); err != nil {
 		return err
 		return err
 	}
 	}
@@ -353,7 +357,7 @@ func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars ma
 	return nil
 	return nil
 }
 }
 
 
-func postContainersCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	config := &Config{}
 	config := &Config{}
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
 		return err
 		return err
@@ -383,7 +387,7 @@ func postContainersCreate(srv *Server, w http.ResponseWriter, r *http.Request, v
 	return nil
 	return nil
 }
 }
 
 
-func postContainersRestart(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 	if err := parseForm(r); err != nil {
 		return err
 		return err
 	}
 	}
@@ -402,7 +406,7 @@ func postContainersRestart(srv *Server, w http.ResponseWriter, r *http.Request,
 	return nil
 	return nil
 }
 }
 
 
-func deleteContainers(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func deleteContainers(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
 	}
 	}
@@ -422,7 +426,7 @@ func deleteContainers(srv *Server, w http.ResponseWriter, r *http.Request, vars
 	return nil
 	return nil
 }
 }
 
 
-func deleteImages(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
@@ -434,7 +438,7 @@ func deleteImages(srv *Server, w http.ResponseWriter, r *http.Request, vars map[
 	return nil
 	return nil
 }
 }
 
 
-func postContainersStart(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
@@ -446,7 +450,7 @@ func postContainersStart(srv *Server, w http.ResponseWriter, r *http.Request, va
 	return nil
 	return nil
 }
 }
 
 
-func postContainersStop(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postContainersStop(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
 	}
 	}
@@ -467,7 +471,7 @@ func postContainersStop(srv *Server, w http.ResponseWriter, r *http.Request, var
 	return nil
 	return nil
 }
 }
 
 
-func postContainersWait(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
@@ -484,7 +488,7 @@ func postContainersWait(srv *Server, w http.ResponseWriter, r *http.Request, var
 	return nil
 	return nil
 }
 }
 
 
-func postContainersAttach(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func postContainersAttach(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
 	}
 	}
@@ -527,7 +531,7 @@ func postContainersAttach(srv *Server, w http.ResponseWriter, r *http.Request, v
 	return nil
 	return nil
 }
 }
 
 
-func getContainersByName(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
@@ -545,7 +549,7 @@ func getContainersByName(srv *Server, w http.ResponseWriter, r *http.Request, va
 	return nil
 	return nil
 }
 }
 
 
-func getImagesByName(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
@@ -563,7 +567,7 @@ func getImagesByName(srv *Server, w http.ResponseWriter, r *http.Request, vars m
 	return nil
 	return nil
 }
 }
 
 
-func postImagesGetCache(srv *Server, 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
@@ -590,7 +594,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
 	r := mux.NewRouter()
 	r := mux.NewRouter()
 	log.Printf("Listening for HTTP on %s\n", addr)
 	log.Printf("Listening for HTTP on %s\n", addr)
 
 
-	m := map[string]map[string]func(*Server, http.ResponseWriter, *http.Request, map[string]string) error{
+	m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{
 		"GET": {
 		"GET": {
 			"/auth":                         getAuth,
 			"/auth":                         getAuth,
 			"/version":                      getVersion,
 			"/version":                      getVersion,
@@ -634,7 +638,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
 			localRoute := route
 			localRoute := route
 			localMethod := method
 			localMethod := method
 			localFct := fct
 			localFct := fct
-			r.Path(localRoute).Methods(localMethod).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			f := func(w http.ResponseWriter, r *http.Request) {
 				utils.Debugf("Calling %s %s", localMethod, localRoute)
 				utils.Debugf("Calling %s %s", localMethod, localRoute)
 				if logging {
 				if logging {
 					log.Println(r.Method, r.RequestURI)
 					log.Println(r.Method, r.RequestURI)
@@ -645,10 +649,20 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
 						utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
 						utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
 					}
 					}
 				}
 				}
-				if err := localFct(srv, w, r, mux.Vars(r)); err != nil {
+				version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
+				if err != nil {
+					version = API_VERSION
+				}
+				if version == 0 || version > API_VERSION {
+					w.WriteHeader(http.StatusNotFound)
+					return
+				}
+				if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil {
 					httpError(w, err)
 					httpError(w, err)
 				}
 				}
-			})
+			}
+			r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
+			r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
 		}
 		}
 	}
 	}
 
 

+ 26 - 26
api_test.go

@@ -48,7 +48,7 @@ func TestGetAuth(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := postAuth(srv, r, req, nil); err != nil {
+	if err := postAuth(srv, API_VERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -74,7 +74,7 @@ func TestGetVersion(t *testing.T) {
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 
 
-	if err := getVersion(srv, r, nil, nil); err != nil {
+	if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -98,7 +98,7 @@ func TestGetInfo(t *testing.T) {
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 
 
-	if err := getInfo(srv, r, nil, nil); err != nil {
+	if err := getInfo(srv, API_VERSION, r, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -129,7 +129,7 @@ func TestGetImagesJson(t *testing.T) {
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 
 
-	if err := getImagesJson(srv, r, req, nil); err != nil {
+	if err := getImagesJson(srv, API_VERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -154,7 +154,7 @@ func TestGetImagesJson(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := getImagesJson(srv, r2, req2, nil); err != nil {
+	if err := getImagesJson(srv, API_VERSION, r2, req2, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -179,7 +179,7 @@ func TestGetImagesJson(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := getImagesJson(srv, r3, req3, nil); err != nil {
+	if err := getImagesJson(srv, API_VERSION, r3, req3, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -200,7 +200,7 @@ func TestGetImagesJson(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	err = getImagesJson(srv, r4, req4, nil)
+	err = getImagesJson(srv, API_VERSION, r4, req4, nil)
 	if err == nil {
 	if err == nil {
 		t.Fatalf("Error expected, received none")
 		t.Fatalf("Error expected, received none")
 	}
 	}
@@ -221,7 +221,7 @@ func TestGetImagesViz(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getImagesViz(srv, r, nil, nil); err != nil {
+	if err := getImagesViz(srv, API_VERSION, r, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -258,7 +258,7 @@ func TestGetImagesSearch(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := getImagesSearch(srv, r, req, nil); err != nil {
+	if err := getImagesSearch(srv, API_VERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -282,7 +282,7 @@ func TestGetImagesHistory(t *testing.T) {
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
 
 
-	if err := getImagesHistory(srv, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
+	if err := getImagesHistory(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -305,7 +305,7 @@ func TestGetImagesByName(t *testing.T) {
 	srv := &Server{runtime: runtime}
 	srv := &Server{runtime: runtime}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getImagesByName(srv, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
+	if err := getImagesByName(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -342,7 +342,7 @@ func TestGetContainersPs(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getContainersPs(srv, r, req, nil); err != nil {
+	if err := getContainersPs(srv, API_VERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	containers := []ApiContainers{}
 	containers := []ApiContainers{}
@@ -385,7 +385,7 @@ func TestGetContainersExport(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err = getContainersExport(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err = getContainersExport(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -440,7 +440,7 @@ func TestGetContainersChanges(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getContainersChanges(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err := getContainersChanges(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	changes := []Change{}
 	changes := []Change{}
@@ -484,7 +484,7 @@ func TestGetContainersByName(t *testing.T) {
 	defer runtime.Destroy(container)
 	defer runtime.Destroy(container)
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getContainersByName(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err := getContainersByName(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	outContainer := &Container{}
 	outContainer := &Container{}
@@ -515,7 +515,7 @@ func TestPostAuth(t *testing.T) {
 	srv.registry.ResetClient(authConfigOrig)
 	srv.registry.ResetClient(authConfigOrig)
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := getAuth(srv, r, nil, nil); err != nil {
+	if err := getAuth(srv, API_VERSION, r, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -562,7 +562,7 @@ func TestPostCommit(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postCommit(srv, r, req, nil); err != nil {
+	if err := postCommit(srv, API_VERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Code != http.StatusCreated {
 	if r.Code != http.StatusCreated {
@@ -840,7 +840,7 @@ func TestPostContainersCreate(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersCreate(srv, r, req, nil); err != nil {
+	if err := postContainersCreate(srv, API_VERSION, r, req, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Code != http.StatusCreated {
 	if r.Code != http.StatusCreated {
@@ -903,7 +903,7 @@ func TestPostContainersKill(t *testing.T) {
 	}
 	}
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersKill(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err := postContainersKill(srv, API_VERSION, 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 {
@@ -951,7 +951,7 @@ func TestPostContainersRestart(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersRestart(srv, r, req, map[string]string{"name": container.Id}); err != nil {
+	if err := postContainersRestart(srv, API_VERSION, 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 {
@@ -992,7 +992,7 @@ func TestPostContainersStart(t *testing.T) {
 	defer runtime.Destroy(container)
 	defer runtime.Destroy(container)
 
 
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersStart(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
+	if err := postContainersStart(srv, API_VERSION, 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 {
@@ -1007,7 +1007,7 @@ func TestPostContainersStart(t *testing.T) {
 	}
 	}
 
 
 	r = httptest.NewRecorder()
 	r = httptest.NewRecorder()
-	if err = postContainersStart(srv, r, nil, map[string]string{"name": container.Id}); err == nil {
+	if err = postContainersStart(srv, API_VERSION, 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")
 	}
 	}
 
 
@@ -1054,7 +1054,7 @@ func TestPostContainersStop(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := postContainersStop(srv, r, req, map[string]string{"name": container.Id}); err != nil {
+	if err := postContainersStop(srv, API_VERSION, 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 {
@@ -1092,7 +1092,7 @@ 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, r, nil, map[string]string{"name": container.Id}); err != nil {
+		if err := postContainersWait(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 		apiWait := &ApiWait{}
 		apiWait := &ApiWait{}
@@ -1154,7 +1154,7 @@ func TestPostContainersAttach(t *testing.T) {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 
 
-		if err := postContainersAttach(srv, r, req, map[string]string{"name": container.Id}); err != nil {
+		if err := postContainersAttach(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 	}()
 	}()
@@ -1224,7 +1224,7 @@ func TestDeleteContainers(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	if err := deleteContainers(srv, r, req, map[string]string{"name": container.Id}); err != nil {
+	if err := deleteContainers(srv, API_VERSION, 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 {

+ 0 - 6
builder_client.go

@@ -32,12 +32,6 @@ type builderClient struct {
 }
 }
 
 
 func (b *builderClient) clearTmp(containers, images map[string]struct{}) {
 func (b *builderClient) clearTmp(containers, images map[string]struct{}) {
-	for c := range containers {
-		if _, _, err := b.cli.call("DELETE", "/containers/"+c, nil); err != nil {
-			utils.Debugf("%s", err)
-		}
-		utils.Debugf("Removing container %s", c)
-	}
 	for i := range images {
 	for i := range images {
 		if _, _, err := b.cli.call("DELETE", "/images/"+i, nil); err != nil {
 		if _, _, err := b.cli.call("DELETE", "/images/"+i, nil); err != nil {
 			utils.Debugf("%s", err)
 			utils.Debugf("%s", err)

+ 7 - 9
commands.go

@@ -24,7 +24,7 @@ import (
 	"unicode"
 	"unicode"
 )
 )
 
 
-const VERSION = "0.3.2"
+const VERSION = "0.3.3"
 
 
 var (
 var (
 	GIT_COMMIT string
 	GIT_COMMIT string
@@ -1167,7 +1167,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", cli.host, cli.port)+path, params)
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%f", cli.host, cli.port, API_VERSION)+path, params)
 	if err != nil {
 	if err != nil {
 		return nil, -1, err
 		return nil, -1, err
 	}
 	}
@@ -1199,7 +1199,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%s", cli.host, cli.port, path), in)
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%f%s", cli.host, cli.port, API_VERSION, path), in)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -1224,7 +1224,6 @@ 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 {
 		type Message struct {
 			Status   string `json:"status,omitempty"`
 			Status   string `json:"status,omitempty"`
 			Progress string `json:"progress,omitempty"`
 			Progress string `json:"progress,omitempty"`
@@ -1237,13 +1236,12 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
 			} else if err != nil {
 			} else if err != nil {
 				return err
 				return err
 			}
 			}
-			if m.Status != "" {
+			if m.Progress != "" {
+				fmt.Fprintf(out, "Downloading %s\r", m.Progress)
+			} else {
 				fmt.Fprintf(out, "%s\n", m.Status)
 				fmt.Fprintf(out, "%s\n", m.Status)
-			} else if m.Progress != "" {
-				fmt.Fprintf(out, "Downloading... %s\r", m.Progress)
 			}
 			}
 		}
 		}
-		fmt.Fprintf(out, "\n")
 	} else {
 	} else {
 		if _, err := io.Copy(out, resp.Body); err != nil {
 		if _, err := io.Copy(out, resp.Body); err != nil {
 			return err
 			return err
@@ -1253,7 +1251,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
 }
 }
 
 
 func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error {
 func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error {
-	req, err := http.NewRequest(method, path, nil)
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%f%s", API_VERSION, path), nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
docs/sources/commandline/command/run.rst

@@ -23,5 +23,5 @@
       -t=false: Allocate a pseudo-tty
       -t=false: Allocate a pseudo-tty
       -u="": Username or UID
       -u="": Username or UID
       -d=[]: Set custom dns servers for the container
       -d=[]: Set custom dns servers for the container
-      -v=[]: Creates a new volumes and mount it at the specified path.
+      -v=[]: Creates a new volume and mounts it at the specified path.
       -volumes-from="": Mount all volumes from the given container.
       -volumes-from="": Mount all volumes from the given container.

+ 43 - 81
docs/sources/installation/kernel.rst

@@ -7,61 +7,29 @@
 Kernel Requirements
 Kernel Requirements
 ===================
 ===================
 
 
+In short, Docker has the following kernel requirements:
+
+- Linux version 3.8 or above.
+
+- `AUFS support <http://aufs.sourceforge.net/>`_.
+
+- Cgroups and namespaces must be enabled.
+
+
   The officially supported kernel is the one recommended by the
   The officially supported kernel is the one recommended by the
   :ref:`ubuntu_linux` installation path. It is the one that most developers
   :ref:`ubuntu_linux` installation path. It is the one that most developers
   will use, and the one that receives the most attention from the core
   will use, and the one that receives the most attention from the core
   contributors. If you decide to go with a different kernel and hit a bug,
   contributors. If you decide to go with a different kernel and hit a bug,
   please try to reproduce it with the official kernels first.
   please try to reproduce it with the official kernels first.
 
 
-If for some reason you cannot or do not want to use the "official" kernels,
+If you cannot or do not want to use the "official" kernels,
 here is some technical background about the features (both optional and
 here is some technical background about the features (both optional and
 mandatory) that docker needs to run successfully.
 mandatory) that docker needs to run successfully.
 
 
-In short, you need kernel version 3.8 (or above), compiled to include
-`AUFS support <http://aufs.sourceforge.net/>`_. Of course, you need to
-enable cgroups and namespaces.
-
-
-Namespaces and Cgroups
-----------------------
-
-You need to enable namespaces and cgroups, to the extend of what is needed
-to run LXC containers. Technically, while namespaces have been introduced
-in the early 2.6 kernels, we do not advise to try any kernel before 2.6.32
-to run LXC containers. Note that 2.6.32 has some documented issues regarding
-network namespace setup and teardown; those issues are not a risk if you
-run containers in a private environment, but can lead to denial-of-service
-attacks if you want to run untrusted code in your containers. For more details,
-see `[LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
-
-Kernels 2.6.38, and every version since 3.2, have been deployed successfully
-to run containerized production workloads. Feature-wise, there is no huge
-improvement between 2.6.38 and up to 3.6 (as far as docker is concerned!).
-
-Starting with version 3.7, the kernel has basic support for
-`Checkpoint/Restore In Userspace <http://criu.org/>`_, which is not used by
-docker at this point, but allows to suspend the state of a container to
-disk and resume it later.
-
-Version 3.8 provides improvements in stability, which are deemed necessary
-for the operation of docker. Versions 3.2 to 3.5 have been shown to
-exhibit a reproducible bug (for more details, see issue
-`#407 <https://github.com/dotcloud/docker/issues/407>`_).
-
-Version 3.8 also brings better support for the
-`setns() syscall <http://lwn.net/Articles/531381/>`_ -- but this should not
-be a concern since docker does not leverage on this feature for now.
-
-If you want a technical overview about those concepts, you might
-want to check those articles on dotCloud's blog:
-`about namespaces <http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part>`_
-and `about cgroups <http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-24-c>`_.
-
-
-Important Note About Pre-3.8 Kernels
-------------------------------------
+Linux version 3.8 or above
+--------------------------
 
 
-As mentioned above, kernels before 3.8 are not stable when used with docker.
+Kernel versions 3.2 to 3.5 are not stable when used with docker.
 In some circumstances, you will experience kernel "oopses", or even crashes.
 In some circumstances, you will experience kernel "oopses", or even crashes.
 The symptoms include:
 The symptoms include:
 
 
@@ -81,6 +49,36 @@ detects something older than 3.8.
 See issue `#407 <https://github.com/dotcloud/docker/issues/407>`_ for details.
 See issue `#407 <https://github.com/dotcloud/docker/issues/407>`_ for details.
 
 
 
 
+AUFS support
+------------
+
+Docker currently relies on AUFS, an unioning filesystem.
+While AUFS is included in the kernels built by the Debian and Ubuntu
+distributions, is not part of the standard kernel. This means that if
+you decide to roll your own kernel, you will have to patch your
+kernel tree to add AUFS. The process is documented on
+`AUFS webpage <http://aufs.sourceforge.net/>`_.
+
+
+Cgroups and namespaces
+----------------------
+
+You need to enable namespaces and cgroups, to the extend of what is needed
+to run LXC containers. Technically, while namespaces have been introduced
+in the early 2.6 kernels, we do not advise to try any kernel before 2.6.32
+to run LXC containers. Note that 2.6.32 has some documented issues regarding
+network namespace setup and teardown; those issues are not a risk if you
+run containers in a private environment, but can lead to denial-of-service
+attacks if you want to run untrusted code in your containers. For more details,
+see `[LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
+
+Kernels 2.6.38, and every version since 3.2, have been deployed successfully
+to run containerized production workloads. Feature-wise, there is no huge
+improvement between 2.6.38 and up to 3.6 (as far as docker is concerned!).
+
+
+
+
 Extra Cgroup Controllers
 Extra Cgroup Controllers
 ------------------------
 ------------------------
 
 
@@ -115,39 +113,3 @@ And replace it by the following one::
     GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount"
     GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount"
 
 
 Then run ``update-grub``, and reboot.
 Then run ``update-grub``, and reboot.
-
-
-AUFS
-----
-
-Docker currently relies on AUFS, an unioning filesystem.
-While AUFS is included in the kernels built by the Debian and Ubuntu
-distributions, is not part of the standard kernel. This means that if
-you decide to roll your own kernel, you will have to patch your
-kernel tree to add AUFS. The process is documented on
-`AUFS webpage <http://aufs.sourceforge.net/>`_.
-
-Note: the AUFS patch is fairly intrusive, but for the record, people have
-successfully applied GRSEC and AUFS together, to obtain hardened production
-kernels.
-
-If you want more information about that topic, there is an
-`article about AUFS on dotCloud's blog 
-<http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-34-a>`_.
-
-
-BTRFS, ZFS, OverlayFS...
-------------------------
-
-There is ongoing development on docker, to implement support for
-`BTRFS <http://en.wikipedia.org/wiki/Btrfs>`_
-(see github issue `#443 <https://github.com/dotcloud/docker/issues/443>`_).
-
-People have also showed interest for `ZFS <http://en.wikipedia.org/wiki/ZFS>`_
-(using e.g. `ZFS-on-Linux <http://zfsonlinux.org/>`_) and OverlayFS.
-The latter is functionally close to AUFS, and it might end up being included
-in the stock kernel; so it's a strong candidate!
-
-Would you like to `contribute
-<https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`_
-support for your favorite filesystem?

+ 29 - 0
docs/website/index.html

@@ -198,6 +198,35 @@
 
 
 
 
 <div class="container">
 <div class="container">
+
+    <div class="row">
+        <div class="span6">
+            <section class="contentblock twitterblock">
+                <img src="https://si0.twimg.com/profile_images/2707460527/252a64411a339184ff375a96fb68dcb0_bigger.png">
+                <em>Mitchell Hashimoto‏@mitchellh:</em> Docker launched today. It is incredible. They’re also working RIGHT NOW on a Vagrant provider. LXC is COMING!!
+            </section>
+        </div>
+        <div class="span6">
+            <section class="contentblock twitterblock">
+                <img src="https://si0.twimg.com/profile_images/1108290260/Adam_Jacob-114x150_original_bigger.jpg">
+                <em>Adam Jacob‏@adamhjk:</em> Docker is clearly the right idea. @solomonstre absolutely killed it. Containerized app deployment is the future, I think.
+            </section>
+        </div>
+    </div>
+    <div class="row">
+        <div class="span6">
+            <section class="contentblock twitterblock">
+                <img src="https://si0.twimg.com/profile_images/14872832/twitter_pic_bigger.jpg">
+                <em>Matt Townsend‏@mtownsend:</em> I have a serious code crush on docker.io - it's Lego for PaaS. Motherfucking awesome Lego.
+            </section>
+        </div>
+        <div class="span6">
+            <section class="contentblock twitterblock">
+                <img src="https://si0.twimg.com/profile_images/1312352395/rupert-259x300_bigger.jpg">
+                <em>Rob Harrop‏@robertharrop:</em> Impressed by @getdocker - it's all kinds of magic. Serious rethink of AWS architecture happening @skillsmatter.
+            </section>
+        </div>
+    </div>
     <div class="row">
     <div class="row">
         <div class="span6">
         <div class="span6">
             <section class="contentblock twitterblock">
             <section class="contentblock twitterblock">

+ 1 - 1
graph.go

@@ -165,7 +165,7 @@ 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)"), tmp.Root)
+	return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)", false), tmp.Root)
 }
 }
 
 
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.

+ 35 - 28
packaging/ubuntu/changelog

@@ -1,37 +1,44 @@
+lxc-docker (0.3.3-1) precise; urgency=low
+  - Registry: Fix push regression
+  - Various bugfixes
+
+ -- dotCloud <ops@dotcloud.com>  Thu, 23 May 2013 00:00:00 -0700
+
+
 lxc-docker (0.3.2-1) precise; urgency=low
 lxc-docker (0.3.2-1) precise; urgency=low
- - Runtime: Store the actual archive on commit
- - Registry: Improve the checksum process
- - Registry: Use the size to have a good progress bar while pushing
- - Registry: Use the actual archive if it exists in order to speed up the push
- - Registry: Fix error 400 on push
+  - Runtime: Store the actual archive on commit
+  - Registry: Improve the checksum process
+  - Registry: Use the size to have a good progress bar while pushing
+  - Registry: Use the actual archive if it exists in order to speed up the push
+  - Registry: Fix error 400 on push
 
 
  -- dotCloud <ops@dotcloud.com>  Fri, 9 May 2013 00:00:00 -0700
  -- dotCloud <ops@dotcloud.com>  Fri, 9 May 2013 00:00:00 -0700
 
 
 
 
 lxc-docker (0.3.1-1) precise; urgency=low
 lxc-docker (0.3.1-1) precise; urgency=low
- - Builder: Implement the autorun capability within docker builder
- - Builder: Add caching to docker builder
- - Builder: Add support for docker builder with native API as top level command
- - Runtime: Add go version to debug infos
- - Builder: Implement ENV within docker builder
- - Registry: Add docker search top level command in order to search a repository
- - Images: output graph of images to dot (graphviz)
- - Documentation: new introduction and high-level overview
- - Documentation: Add the documentation for docker builder
- - Website: new high-level overview
- - Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc
- - Images: fix ByParent function
- - Builder: Check the command existance prior create and add Unit tests for the case
- - Registry: Fix pull for official images with specific tag
- - Registry: Fix issue when login in with a different user and trying to push
- - Documentation: CSS fix for docker documentation to make REST API docs look better.
- - Documentation: Fixed CouchDB example page header mistake
- - Documentation: fixed README formatting
- - Registry: Improve checksum - async calculation
- - Runtime: kernel version - don't show the dash if flavor is empty
- - Documentation: updated www.docker.io website.
- - Builder: use any whitespaces instead of tabs
- - Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker
+  - Builder: Implement the autorun capability within docker builder
+  - Builder: Add caching to docker builder
+  - Builder: Add support for docker builder with native API as top level command
+  - Runtime: Add go version to debug infos
+  - Builder: Implement ENV within docker builder
+  - Registry: Add docker search top level command in order to search a repository
+  - Images: output graph of images to dot (graphviz)
+  - Documentation: new introduction and high-level overview
+  - Documentation: Add the documentation for docker builder
+  - Website: new high-level overview
+  - Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc
+  - Images: fix ByParent function
+  - Builder: Check the command existance prior create and add Unit tests for the case
+  - Registry: Fix pull for official images with specific tag
+  - Registry: Fix issue when login in with a different user and trying to push
+  - Documentation: CSS fix for docker documentation to make REST API docs look better.
+  - Documentation: Fixed CouchDB example page header mistake
+  - Documentation: fixed README formatting
+  - Registry: Improve checksum - async calculation
+  - Runtime: kernel version - don't show the dash if flavor is empty
+  - Documentation: updated www.docker.io website.
+  - Builder: use any whitespaces instead of tabs
+  - Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker
 
 
  -- dotCloud <ops@dotcloud.com>  Fri, 8 May 2013 00:00:00 -0700
  -- dotCloud <ops@dotcloud.com>  Fri, 8 May 2013 00:00:00 -0700
 
 

+ 6 - 1
registry/registry.go

@@ -428,9 +428,14 @@ func (r *Registry) ResetClient(authConfig *auth.AuthConfig) {
 	r.client.Jar = cookiejar.NewCookieJar()
 	r.client.Jar = cookiejar.NewCookieJar()
 }
 }
 
 
-func (r *Registry) GetAuthConfig() *auth.AuthConfig {
+func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig {
+	password := ""
+	if withPasswd {
+		password = r.authConfig.Password
+	}
 	return &auth.AuthConfig{
 	return &auth.AuthConfig{
 		Username: r.authConfig.Username,
 		Username: r.authConfig.Username,
+		Password: password,
 		Email:    r.authConfig.Email,
 		Email:    r.authConfig.Email,
 	}
 	}
 }
 }

+ 17 - 15
server.go

@@ -91,7 +91,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error {
 		return err
 		return err
 	}
 	}
 
 
-	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil {
+	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)\r", false), path); err != nil {
 		return err
 		return err
 	}
 	}
 	// FIXME: Handle custom repo, tag comment, author
 	// FIXME: Handle custom repo, tag comment, author
@@ -291,16 +291,17 @@ func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string) error {
+func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string, json bool) error {
 	history, err := srv.registry.GetRemoteHistory(imgId, registry, token)
 	history, err := srv.registry.GetRemoteHistory(imgId, registry, token)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+
 	// FIXME: Try to stream the images?
 	// FIXME: Try to stream the images?
 	// 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, "{\"status\" :\"Pulling %s metadata\"}", id)
+			fmt.Fprintf(out, utils.FormatStatus("Pulling %s metadata", json), id)
 			imgJson, err := srv.registry.GetRemoteImageJson(id, registry, token)
 			imgJson, err := srv.registry.GetRemoteImageJson(id, registry, token)
 			if err != nil {
 			if err != nil {
 				// FIXME: Keep goging in case of error?
 				// FIXME: Keep goging in case of error?
@@ -312,12 +313,12 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
 			}
 			}
 
 
 			// Get the layer
 			// Get the layer
-			fmt.Fprintf(out, "{\"status\" :\"Pulling %s fs layer\"}", img.Id)
+			fmt.Fprintf(out, utils.FormatStatus("Pulling %s fs layer", json), id)
 			layer, contentLength, err := srv.registry.GetRemoteImageLayer(img.Id, registry, token)
 			layer, contentLength, err := srv.registry.GetRemoteImageLayer(img.Id, registry, token)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
-			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, ""), false, img); err != nil {
+			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, utils.FormatProgress("%v/%v (%v)", json), json), false, img); err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}
@@ -325,8 +326,8 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error {
-	fmt.Fprintf(out, "{\"status\":\"Pulling repository %s from %s\"}", remote, auth.IndexServerAddress())
+func (srv *Server) pullRepository(out io.Writer, remote, askedTag string, json bool) error {
+	fmt.Fprintf(out, utils.FormatStatus("Pulling repository %s from %s", json), remote, auth.IndexServerAddress())
 	repoData, err := srv.registry.GetRepositoryData(remote)
 	repoData, err := srv.registry.GetRepositoryData(remote)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -363,11 +364,11 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error
 			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, "{\"status\":\"Pulling image %s (%s) from %s\"}", img.Id, img.Tag, remote)
+		fmt.Fprintf(out, utils.FormatStatus("Pulling image %s (%s) from %s", json), img.Id, img.Tag, remote)
 		success := false
 		success := false
 		for _, ep := range repoData.Endpoints {
 		for _, ep := range repoData.Endpoints {
-			if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil {
-				fmt.Fprintf(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err)
+			if err := srv.pullImage(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)
 				continue
 				continue
 			}
 			}
 			success = true
 			success = true
@@ -392,18 +393,19 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) ImagePull(name, tag, registry string, out io.Writer) error {
+func (srv *Server) ImagePull(name, tag, registry string, out io.Writer, json bool) error {
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
 	if registry != "" {
 	if registry != "" {
-		if err := srv.pullImage(out, name, registry, nil); err != nil {
+		if err := srv.pullImage(out, name, registry, nil, json); err != nil {
 			return err
 			return err
 		}
 		}
 		return nil
 		return nil
 	}
 	}
 
 
-	if err := srv.pullRepository(out, name, tag); err != nil {
+	if err := srv.pullRepository(out, name, tag, json); err != nil {
 		return err
 		return err
 	}
 	}
+
 	return nil
 	return nil
 }
 }
 
 
@@ -567,7 +569,7 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st
 	}
 	}
 
 
 	// Send the layer
 	// Send the layer
-	if err := srv.registry.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, ""), ep, token); err != nil {
+	if err := srv.registry.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, "", false), ep, token); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -618,7 +620,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)")
+		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)\r", false)
 	}
 	}
 	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 {

+ 2 - 2
utils.go

@@ -49,10 +49,10 @@ func CompareConfig(a, b *Config) bool {
 }
 }
 
 
 func MergeConfig(userConf, imageConf *Config) {
 func MergeConfig(userConf, imageConf *Config) {
-	if userConf.Hostname != "" {
+	if userConf.Hostname == "" {
 		userConf.Hostname = imageConf.Hostname
 		userConf.Hostname = imageConf.Hostname
 	}
 	}
-	if userConf.User != "" {
+	if userConf.User == "" {
 		userConf.User = imageConf.User
 		userConf.User = imageConf.User
 	}
 	}
 	if userConf.Memory == 0 {
 	if userConf.Memory == 0 {

+ 28 - 6
utils/utils.go

@@ -69,6 +69,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
 }
 }
 
 
 func (r *progressReader) Read(p []byte) (n int, err error) {
 func (r *progressReader) Read(p []byte) (n int, err error) {
@@ -84,22 +85,27 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
 	}
 	}
 	if r.readProgress-r.lastUpdate > updateEvery || err != nil {
 	if r.readProgress-r.lastUpdate > updateEvery || err != nil {
 		if r.readTotal > 0 {
 		if r.readTotal > 0 {
-			fmt.Fprintf(r.output, r.template, r.readProgress, r.readTotal)
+			fmt.Fprintf(r.output, r.template, r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
 		} else {
 		} else {
-			fmt.Fprintf(r.output, r.template, r.readProgress, "?")
+			fmt.Fprintf(r.output, r.template, r.readProgress, "?", "n/a")
 		}
 		}
 		r.lastUpdate = r.readProgress
 		r.lastUpdate = r.readProgress
 	}
 	}
+	// Send newline when complete
+	if err != nil {
+		fmt.Fprintf(r.output, FormatStatus("", r.json))
+	}
+
 	return read, err
 	return read, err
 }
 }
 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) *progressReader {
-	if template == "" {
-		template = "{\"progress\":\"%v/%v\"}"
+func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string, json bool) *progressReader {
+      	if template == "" {
+		template = "%v/%v (%v)\r"
 	}
 	}
-	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template}
+	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template, json}
 }
 }
 
 
 // HumanDuration returns a human-readable approximation of a duration
 // HumanDuration returns a human-readable approximation of a duration
@@ -550,3 +556,19 @@ 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 + "\"}"
+	}
+	return str + "\r\n"
+}
+
+func FormatProgress(str string, json bool) string {
+	if json {
+		return "{\"progress\" : \"" + str + "\"}"
+	}
+	return "Downloading " + str + "\r"
+}
+
+