Browse code

first version of Pull

Victor Vieux authored on 2013/05/24 23:43:52
Showing 14 changed files
... ...
@@ -1,5 +1,9 @@
1 1
 # Changelog
2 2
 
3
+## 0.3.3 (2013-05-23)
4
+ - Registry: Fix push regression
5
+ - Various bugfixes
6
+
3 7
 ## 0.3.2 (2013-05-09)
4 8
  * Runtime: Store the actual archive on commit
5 9
  * Registry: Improve the checksum process
... ...
@@ -13,6 +13,8 @@ import (
13 13
 	"strings"
14 14
 )
15 15
 
16
+const API_VERSION = 1.1
17
+
16 18
 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
17 19
 	conn, _, err := w.(http.Hijacker).Hijack()
18 20
 	if err != nil {
... ...
@@ -56,8 +58,8 @@ func getBoolParam(value string) (bool, error) {
56 56
 	return false, fmt.Errorf("Bad parameter")
57 57
 }
58 58
 
59
-func getAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
60
-	b, err := json.Marshal(srv.registry.GetAuthConfig())
59
+func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
60
+	b, err := json.Marshal(srv.registry.GetAuthConfig(false))
61 61
 	if err != nil {
62 62
 		return err
63 63
 	}
... ...
@@ -65,14 +67,14 @@ func getAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[strin
65 65
 	return nil
66 66
 }
67 67
 
68
-func postAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
68
+func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
69 69
 	config := &auth.AuthConfig{}
70 70
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
71 71
 		return err
72 72
 	}
73
-
74
-	if config.Username == srv.registry.GetAuthConfig().Username {
75
-		config.Password = srv.registry.GetAuthConfig().Password
73
+	authConfig := srv.registry.GetAuthConfig(true)
74
+	if config.Username == authConfig.Username {
75
+		config.Password = authConfig.Password
76 76
 	}
77 77
 
78 78
 	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
94 94
 	return nil
95 95
 }
96 96
 
97
-func getVersion(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
97
+func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
98 98
 	m := srv.DockerVersion()
99 99
 	b, err := json.Marshal(m)
100 100
 	if err != nil {
... ...
@@ -104,7 +106,7 @@ func getVersion(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st
104 104
 	return nil
105 105
 }
106 106
 
107
-func postContainersKill(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
107
+func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
108 108
 	if vars == nil {
109 109
 		return fmt.Errorf("Missing parameter")
110 110
 	}
... ...
@@ -116,7 +118,7 @@ func postContainersKill(srv *Server, w http.ResponseWriter, r *http.Request, var
116 116
 	return nil
117 117
 }
118 118
 
119
-func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
119
+func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
120 120
 	if vars == nil {
121 121
 		return fmt.Errorf("Missing parameter")
122 122
 	}
... ...
@@ -129,7 +131,7 @@ func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, va
129 129
 	return nil
130 130
 }
131 131
 
132
-func getImagesJson(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
132
+func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
133 133
 	if err := parseForm(r); err != nil {
134 134
 		return err
135 135
 	}
... ...
@@ -152,14 +154,14 @@ func getImagesJson(srv *Server, w http.ResponseWriter, r *http.Request, vars map
152 152
 	return nil
153 153
 }
154 154
 
155
-func getImagesViz(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
155
+func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
156 156
 	if err := srv.ImagesViz(w); err != nil {
157 157
 		return err
158 158
 	}
159 159
 	return nil
160 160
 }
161 161
 
162
-func getInfo(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
162
+func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
163 163
 	out := srv.DockerInfo()
164 164
 	b, err := json.Marshal(out)
165 165
 	if err != nil {
... ...
@@ -169,7 +171,7 @@ func getInfo(srv *Server, w http.ResponseWriter, r *http.Request, vars map[strin
169 169
 	return nil
170 170
 }
171 171
 
172
-func getImagesHistory(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
172
+func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
173 173
 	if vars == nil {
174 174
 		return fmt.Errorf("Missing parameter")
175 175
 	}
... ...
@@ -186,7 +188,7 @@ func getImagesHistory(srv *Server, w http.ResponseWriter, r *http.Request, vars
186 186
 	return nil
187 187
 }
188 188
 
189
-func getContainersChanges(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
189
+func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
190 190
 	if vars == nil {
191 191
 		return fmt.Errorf("Missing parameter")
192 192
 	}
... ...
@@ -203,7 +205,7 @@ func getContainersChanges(srv *Server, w http.ResponseWriter, r *http.Request, v
203 203
 	return nil
204 204
 }
205 205
 
206
-func getContainersPs(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
206
+func getContainersPs(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
207 207
 	if err := parseForm(r); err != nil {
208 208
 		return err
209 209
 	}
... ...
@@ -227,7 +229,7 @@ func getContainersPs(srv *Server, w http.ResponseWriter, r *http.Request, vars m
227 227
 	return nil
228 228
 }
229 229
 
230
-func postImagesTag(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
230
+func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
231 231
 	if err := parseForm(r); err != nil {
232 232
 		return err
233 233
 	}
... ...
@@ -249,7 +251,7 @@ func postImagesTag(srv *Server, w http.ResponseWriter, r *http.Request, vars map
249 249
 	return nil
250 250
 }
251 251
 
252
-func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
252
+func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
253 253
 	if err := parseForm(r); err != nil {
254 254
 		return err
255 255
 	}
... ...
@@ -276,7 +278,7 @@ func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st
276 276
 }
277 277
 
278 278
 // Creates an image from Pull or from Import
279
-func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
279
+func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
280 280
 	if err := parseForm(r); err != nil {
281 281
 		return err
282 282
 	}
... ...
@@ -288,8 +290,10 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars
288 288
 
289 289
 	if image != "" { //pull
290 290
 		registry := r.Form.Get("registry")
291
-		w.Header().Set("Content-Type", "application/json")
292
-		if err := srv.ImagePull(image, tag, registry, w); err != nil {
291
+		if version > 1.0 {
292
+			w.Header().Set("Content-Type", "application/json")
293
+		}
294
+		if err := srv.ImagePull(image, tag, registry, w, version > 1.0); err != nil {
293 295
 			return err
294 296
 		}
295 297
 	} else { //import
... ...
@@ -300,7 +304,7 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars
300 300
 	return nil
301 301
 }
302 302
 
303
-func getImagesSearch(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
303
+func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
304 304
 	if err := parseForm(r); err != nil {
305 305
 		return err
306 306
 	}
... ...
@@ -318,7 +322,7 @@ func getImagesSearch(srv *Server, w http.ResponseWriter, r *http.Request, vars m
318 318
 	return nil
319 319
 }
320 320
 
321
-func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
321
+func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
322 322
 	if err := parseForm(r); err != nil {
323 323
 		return err
324 324
 	}
... ...
@@ -336,7 +340,7 @@ func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars
336 336
 	return nil
337 337
 }
338 338
 
339
-func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
339
+func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
340 340
 	if err := parseForm(r); err != nil {
341 341
 		return err
342 342
 	}
... ...
@@ -353,7 +357,7 @@ func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars ma
353 353
 	return nil
354 354
 }
355 355
 
356
-func postContainersCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
356
+func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
357 357
 	config := &Config{}
358 358
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
359 359
 		return err
... ...
@@ -383,7 +387,7 @@ func postContainersCreate(srv *Server, w http.ResponseWriter, r *http.Request, v
383 383
 	return nil
384 384
 }
385 385
 
386
-func postContainersRestart(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
386
+func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
387 387
 	if err := parseForm(r); err != nil {
388 388
 		return err
389 389
 	}
... ...
@@ -402,7 +406,7 @@ func postContainersRestart(srv *Server, w http.ResponseWriter, r *http.Request,
402 402
 	return nil
403 403
 }
404 404
 
405
-func deleteContainers(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
405
+func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
406 406
 	if err := parseForm(r); err != nil {
407 407
 		return err
408 408
 	}
... ...
@@ -422,7 +426,7 @@ func deleteContainers(srv *Server, w http.ResponseWriter, r *http.Request, vars
422 422
 	return nil
423 423
 }
424 424
 
425
-func deleteImages(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
425
+func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
426 426
 	if vars == nil {
427 427
 		return fmt.Errorf("Missing parameter")
428 428
 	}
... ...
@@ -434,7 +438,7 @@ func deleteImages(srv *Server, w http.ResponseWriter, r *http.Request, vars map[
434 434
 	return nil
435 435
 }
436 436
 
437
-func postContainersStart(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
437
+func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
438 438
 	if vars == nil {
439 439
 		return fmt.Errorf("Missing parameter")
440 440
 	}
... ...
@@ -446,7 +450,7 @@ func postContainersStart(srv *Server, w http.ResponseWriter, r *http.Request, va
446 446
 	return nil
447 447
 }
448 448
 
449
-func postContainersStop(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
449
+func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
450 450
 	if err := parseForm(r); err != nil {
451 451
 		return err
452 452
 	}
... ...
@@ -467,7 +471,7 @@ func postContainersStop(srv *Server, w http.ResponseWriter, r *http.Request, var
467 467
 	return nil
468 468
 }
469 469
 
470
-func postContainersWait(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
470
+func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
471 471
 	if vars == nil {
472 472
 		return fmt.Errorf("Missing parameter")
473 473
 	}
... ...
@@ -484,7 +488,7 @@ func postContainersWait(srv *Server, w http.ResponseWriter, r *http.Request, var
484 484
 	return nil
485 485
 }
486 486
 
487
-func postContainersAttach(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
487
+func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
488 488
 	if err := parseForm(r); err != nil {
489 489
 		return err
490 490
 	}
... ...
@@ -527,7 +531,7 @@ func postContainersAttach(srv *Server, w http.ResponseWriter, r *http.Request, v
527 527
 	return nil
528 528
 }
529 529
 
530
-func getContainersByName(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
530
+func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
531 531
 	if vars == nil {
532 532
 		return fmt.Errorf("Missing parameter")
533 533
 	}
... ...
@@ -545,7 +549,7 @@ func getContainersByName(srv *Server, w http.ResponseWriter, r *http.Request, va
545 545
 	return nil
546 546
 }
547 547
 
548
-func getImagesByName(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
548
+func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
549 549
 	if vars == nil {
550 550
 		return fmt.Errorf("Missing parameter")
551 551
 	}
... ...
@@ -563,7 +567,7 @@ func getImagesByName(srv *Server, w http.ResponseWriter, r *http.Request, vars m
563 563
 	return nil
564 564
 }
565 565
 
566
-func postImagesGetCache(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
566
+func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
567 567
 	apiConfig := &ApiImageConfig{}
568 568
 	if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
569 569
 		return err
... ...
@@ -590,7 +594,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
590 590
 	r := mux.NewRouter()
591 591
 	log.Printf("Listening for HTTP on %s\n", addr)
592 592
 
593
-	m := map[string]map[string]func(*Server, http.ResponseWriter, *http.Request, map[string]string) error{
593
+	m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{
594 594
 		"GET": {
595 595
 			"/auth":                         getAuth,
596 596
 			"/version":                      getVersion,
... ...
@@ -634,7 +638,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
634 634
 			localRoute := route
635 635
 			localMethod := method
636 636
 			localFct := fct
637
-			r.Path(localRoute).Methods(localMethod).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
637
+			f := func(w http.ResponseWriter, r *http.Request) {
638 638
 				utils.Debugf("Calling %s %s", localMethod, localRoute)
639 639
 				if logging {
640 640
 					log.Println(r.Method, r.RequestURI)
... ...
@@ -645,10 +649,20 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
645 645
 						utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
646 646
 					}
647 647
 				}
648
-				if err := localFct(srv, w, r, mux.Vars(r)); err != nil {
648
+				version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
649
+				if err != nil {
650
+					version = API_VERSION
651
+				}
652
+				if version == 0 || version > API_VERSION {
653
+					w.WriteHeader(http.StatusNotFound)
654
+					return
655
+				}
656
+				if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil {
649 657
 					httpError(w, err)
650 658
 				}
651
-			})
659
+			}
660
+			r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
661
+			r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
652 662
 		}
653 663
 	}
654 664
 
... ...
@@ -48,7 +48,7 @@ func TestGetAuth(t *testing.T) {
48 48
 		t.Fatal(err)
49 49
 	}
50 50
 
51
-	if err := postAuth(srv, r, req, nil); err != nil {
51
+	if err := postAuth(srv, API_VERSION, r, req, nil); err != nil {
52 52
 		t.Fatal(err)
53 53
 	}
54 54
 
... ...
@@ -74,7 +74,7 @@ func TestGetVersion(t *testing.T) {
74 74
 
75 75
 	r := httptest.NewRecorder()
76 76
 
77
-	if err := getVersion(srv, r, nil, nil); err != nil {
77
+	if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil {
78 78
 		t.Fatal(err)
79 79
 	}
80 80
 
... ...
@@ -98,7 +98,7 @@ func TestGetInfo(t *testing.T) {
98 98
 
99 99
 	r := httptest.NewRecorder()
100 100
 
101
-	if err := getInfo(srv, r, nil, nil); err != nil {
101
+	if err := getInfo(srv, API_VERSION, r, nil, nil); err != nil {
102 102
 		t.Fatal(err)
103 103
 	}
104 104
 
... ...
@@ -129,7 +129,7 @@ func TestGetImagesJson(t *testing.T) {
129 129
 
130 130
 	r := httptest.NewRecorder()
131 131
 
132
-	if err := getImagesJson(srv, r, req, nil); err != nil {
132
+	if err := getImagesJson(srv, API_VERSION, r, req, nil); err != nil {
133 133
 		t.Fatal(err)
134 134
 	}
135 135
 
... ...
@@ -154,7 +154,7 @@ func TestGetImagesJson(t *testing.T) {
154 154
 		t.Fatal(err)
155 155
 	}
156 156
 
157
-	if err := getImagesJson(srv, r2, req2, nil); err != nil {
157
+	if err := getImagesJson(srv, API_VERSION, r2, req2, nil); err != nil {
158 158
 		t.Fatal(err)
159 159
 	}
160 160
 
... ...
@@ -179,7 +179,7 @@ func TestGetImagesJson(t *testing.T) {
179 179
 		t.Fatal(err)
180 180
 	}
181 181
 
182
-	if err := getImagesJson(srv, r3, req3, nil); err != nil {
182
+	if err := getImagesJson(srv, API_VERSION, r3, req3, nil); err != nil {
183 183
 		t.Fatal(err)
184 184
 	}
185 185
 
... ...
@@ -200,7 +200,7 @@ func TestGetImagesJson(t *testing.T) {
200 200
 		t.Fatal(err)
201 201
 	}
202 202
 
203
-	err = getImagesJson(srv, r4, req4, nil)
203
+	err = getImagesJson(srv, API_VERSION, r4, req4, nil)
204 204
 	if err == nil {
205 205
 		t.Fatalf("Error expected, received none")
206 206
 	}
... ...
@@ -221,7 +221,7 @@ func TestGetImagesViz(t *testing.T) {
221 221
 	srv := &Server{runtime: runtime}
222 222
 
223 223
 	r := httptest.NewRecorder()
224
-	if err := getImagesViz(srv, r, nil, nil); err != nil {
224
+	if err := getImagesViz(srv, API_VERSION, r, nil, nil); err != nil {
225 225
 		t.Fatal(err)
226 226
 	}
227 227
 
... ...
@@ -258,7 +258,7 @@ func TestGetImagesSearch(t *testing.T) {
258 258
 		t.Fatal(err)
259 259
 	}
260 260
 
261
-	if err := getImagesSearch(srv, r, req, nil); err != nil {
261
+	if err := getImagesSearch(srv, API_VERSION, r, req, nil); err != nil {
262 262
 		t.Fatal(err)
263 263
 	}
264 264
 
... ...
@@ -282,7 +282,7 @@ func TestGetImagesHistory(t *testing.T) {
282 282
 
283 283
 	r := httptest.NewRecorder()
284 284
 
285
-	if err := getImagesHistory(srv, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
285
+	if err := getImagesHistory(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
286 286
 		t.Fatal(err)
287 287
 	}
288 288
 
... ...
@@ -305,7 +305,7 @@ func TestGetImagesByName(t *testing.T) {
305 305
 	srv := &Server{runtime: runtime}
306 306
 
307 307
 	r := httptest.NewRecorder()
308
-	if err := getImagesByName(srv, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
308
+	if err := getImagesByName(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
309 309
 		t.Fatal(err)
310 310
 	}
311 311
 
... ...
@@ -342,7 +342,7 @@ func TestGetContainersPs(t *testing.T) {
342 342
 	}
343 343
 
344 344
 	r := httptest.NewRecorder()
345
-	if err := getContainersPs(srv, r, req, nil); err != nil {
345
+	if err := getContainersPs(srv, API_VERSION, r, req, nil); err != nil {
346 346
 		t.Fatal(err)
347 347
 	}
348 348
 	containers := []ApiContainers{}
... ...
@@ -385,7 +385,7 @@ func TestGetContainersExport(t *testing.T) {
385 385
 	}
386 386
 
387 387
 	r := httptest.NewRecorder()
388
-	if err = getContainersExport(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
388
+	if err = getContainersExport(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
389 389
 		t.Fatal(err)
390 390
 	}
391 391
 
... ...
@@ -440,7 +440,7 @@ func TestGetContainersChanges(t *testing.T) {
440 440
 	}
441 441
 
442 442
 	r := httptest.NewRecorder()
443
-	if err := getContainersChanges(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
443
+	if err := getContainersChanges(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
444 444
 		t.Fatal(err)
445 445
 	}
446 446
 	changes := []Change{}
... ...
@@ -484,7 +484,7 @@ func TestGetContainersByName(t *testing.T) {
484 484
 	defer runtime.Destroy(container)
485 485
 
486 486
 	r := httptest.NewRecorder()
487
-	if err := getContainersByName(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
487
+	if err := getContainersByName(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
488 488
 		t.Fatal(err)
489 489
 	}
490 490
 	outContainer := &Container{}
... ...
@@ -515,7 +515,7 @@ func TestPostAuth(t *testing.T) {
515 515
 	srv.registry.ResetClient(authConfigOrig)
516 516
 
517 517
 	r := httptest.NewRecorder()
518
-	if err := getAuth(srv, r, nil, nil); err != nil {
518
+	if err := getAuth(srv, API_VERSION, r, nil, nil); err != nil {
519 519
 		t.Fatal(err)
520 520
 	}
521 521
 
... ...
@@ -562,7 +562,7 @@ func TestPostCommit(t *testing.T) {
562 562
 	}
563 563
 
564 564
 	r := httptest.NewRecorder()
565
-	if err := postCommit(srv, r, req, nil); err != nil {
565
+	if err := postCommit(srv, API_VERSION, r, req, nil); err != nil {
566 566
 		t.Fatal(err)
567 567
 	}
568 568
 	if r.Code != http.StatusCreated {
... ...
@@ -840,7 +840,7 @@ func TestPostContainersCreate(t *testing.T) {
840 840
 	}
841 841
 
842 842
 	r := httptest.NewRecorder()
843
-	if err := postContainersCreate(srv, r, req, nil); err != nil {
843
+	if err := postContainersCreate(srv, API_VERSION, r, req, nil); err != nil {
844 844
 		t.Fatal(err)
845 845
 	}
846 846
 	if r.Code != http.StatusCreated {
... ...
@@ -903,7 +903,7 @@ func TestPostContainersKill(t *testing.T) {
903 903
 	}
904 904
 
905 905
 	r := httptest.NewRecorder()
906
-	if err := postContainersKill(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
906
+	if err := postContainersKill(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
907 907
 		t.Fatal(err)
908 908
 	}
909 909
 	if r.Code != http.StatusNoContent {
... ...
@@ -951,7 +951,7 @@ func TestPostContainersRestart(t *testing.T) {
951 951
 		t.Fatal(err)
952 952
 	}
953 953
 	r := httptest.NewRecorder()
954
-	if err := postContainersRestart(srv, r, req, map[string]string{"name": container.Id}); err != nil {
954
+	if err := postContainersRestart(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
955 955
 		t.Fatal(err)
956 956
 	}
957 957
 	if r.Code != http.StatusNoContent {
... ...
@@ -992,7 +992,7 @@ func TestPostContainersStart(t *testing.T) {
992 992
 	defer runtime.Destroy(container)
993 993
 
994 994
 	r := httptest.NewRecorder()
995
-	if err := postContainersStart(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
995
+	if err := postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
996 996
 		t.Fatal(err)
997 997
 	}
998 998
 	if r.Code != http.StatusNoContent {
... ...
@@ -1007,7 +1007,7 @@ func TestPostContainersStart(t *testing.T) {
1007 1007
 	}
1008 1008
 
1009 1009
 	r = httptest.NewRecorder()
1010
-	if err = postContainersStart(srv, r, nil, map[string]string{"name": container.Id}); err == nil {
1010
+	if err = postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err == nil {
1011 1011
 		t.Fatalf("A running containter should be able to be started")
1012 1012
 	}
1013 1013
 
... ...
@@ -1054,7 +1054,7 @@ func TestPostContainersStop(t *testing.T) {
1054 1054
 		t.Fatal(err)
1055 1055
 	}
1056 1056
 	r := httptest.NewRecorder()
1057
-	if err := postContainersStop(srv, r, req, map[string]string{"name": container.Id}); err != nil {
1057
+	if err := postContainersStop(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
1058 1058
 		t.Fatal(err)
1059 1059
 	}
1060 1060
 	if r.Code != http.StatusNoContent {
... ...
@@ -1092,7 +1092,7 @@ func TestPostContainersWait(t *testing.T) {
1092 1092
 
1093 1093
 	setTimeout(t, "Wait timed out", 3*time.Second, func() {
1094 1094
 		r := httptest.NewRecorder()
1095
-		if err := postContainersWait(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
1095
+		if err := postContainersWait(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
1096 1096
 			t.Fatal(err)
1097 1097
 		}
1098 1098
 		apiWait := &ApiWait{}
... ...
@@ -1154,7 +1154,7 @@ func TestPostContainersAttach(t *testing.T) {
1154 1154
 			t.Fatal(err)
1155 1155
 		}
1156 1156
 
1157
-		if err := postContainersAttach(srv, r, req, map[string]string{"name": container.Id}); err != nil {
1157
+		if err := postContainersAttach(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
1158 1158
 			t.Fatal(err)
1159 1159
 		}
1160 1160
 	}()
... ...
@@ -1224,7 +1224,7 @@ func TestDeleteContainers(t *testing.T) {
1224 1224
 		t.Fatal(err)
1225 1225
 	}
1226 1226
 	r := httptest.NewRecorder()
1227
-	if err := deleteContainers(srv, r, req, map[string]string{"name": container.Id}); err != nil {
1227
+	if err := deleteContainers(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
1228 1228
 		t.Fatal(err)
1229 1229
 	}
1230 1230
 	if r.Code != http.StatusNoContent {
... ...
@@ -32,12 +32,6 @@ type builderClient struct {
32 32
 }
33 33
 
34 34
 func (b *builderClient) clearTmp(containers, images map[string]struct{}) {
35
-	for c := range containers {
36
-		if _, _, err := b.cli.call("DELETE", "/containers/"+c, nil); err != nil {
37
-			utils.Debugf("%s", err)
38
-		}
39
-		utils.Debugf("Removing container %s", c)
40
-	}
41 35
 	for i := range images {
42 36
 		if _, _, err := b.cli.call("DELETE", "/images/"+i, nil); err != nil {
43 37
 			utils.Debugf("%s", err)
... ...
@@ -24,7 +24,7 @@ import (
24 24
 	"unicode"
25 25
 )
26 26
 
27
-const VERSION = "0.3.2"
27
+const VERSION = "0.3.3"
28 28
 
29 29
 var (
30 30
 	GIT_COMMIT string
... ...
@@ -1167,7 +1167,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
1167 1167
 		params = bytes.NewBuffer(buf)
1168 1168
 	}
1169 1169
 
1170
-	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d", cli.host, cli.port)+path, params)
1170
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%f", cli.host, cli.port, API_VERSION)+path, params)
1171 1171
 	if err != nil {
1172 1172
 		return nil, -1, err
1173 1173
 	}
... ...
@@ -1199,7 +1199,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1199 1199
 	if (method == "POST" || method == "PUT") && in == nil {
1200 1200
 		in = bytes.NewReader([]byte{})
1201 1201
 	}
1202
-	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, path), in)
1202
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%f%s", cli.host, cli.port, API_VERSION, path), in)
1203 1203
 	if err != nil {
1204 1204
 		return err
1205 1205
 	}
... ...
@@ -1224,7 +1224,6 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1224 1224
 	}
1225 1225
 
1226 1226
 	if resp.Header.Get("Content-Type") == "application/json" {
1227
-
1228 1227
 		type Message struct {
1229 1228
 			Status   string `json:"status,omitempty"`
1230 1229
 			Progress string `json:"progress,omitempty"`
... ...
@@ -1237,13 +1236,12 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1237 1237
 			} else if err != nil {
1238 1238
 				return err
1239 1239
 			}
1240
-			if m.Status != "" {
1240
+			if m.Progress != "" {
1241
+				fmt.Fprintf(out, "Downloading %s\r", m.Progress)
1242
+			} else {
1241 1243
 				fmt.Fprintf(out, "%s\n", m.Status)
1242
-			} else if m.Progress != "" {
1243
-				fmt.Fprintf(out, "Downloading... %s\r", m.Progress)
1244 1244
 			}
1245 1245
 		}
1246
-		fmt.Fprintf(out, "\n")
1247 1246
 	} else {
1248 1247
 		if _, err := io.Copy(out, resp.Body); err != nil {
1249 1248
 			return err
... ...
@@ -1253,7 +1251,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1253 1253
 }
1254 1254
 
1255 1255
 func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error {
1256
-	req, err := http.NewRequest(method, path, nil)
1256
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%f%s", API_VERSION, path), nil)
1257 1257
 	if err != nil {
1258 1258
 		return err
1259 1259
 	}
... ...
@@ -23,5 +23,5 @@
23 23
       -t=false: Allocate a pseudo-tty
24 24
       -u="": Username or UID
25 25
       -d=[]: Set custom dns servers for the container
26
-      -v=[]: Creates a new volumes and mount it at the specified path.
26
+      -v=[]: Creates a new volume and mounts it at the specified path.
27 27
       -volumes-from="": Mount all volumes from the given container.
... ...
@@ -7,61 +7,29 @@
7 7
 Kernel Requirements
8 8
 ===================
9 9
 
10
+In short, Docker has the following kernel requirements:
11
+
12
+- Linux version 3.8 or above.
13
+
14
+- `AUFS support <http://aufs.sourceforge.net/>`_.
15
+
16
+- Cgroups and namespaces must be enabled.
17
+
18
+
10 19
   The officially supported kernel is the one recommended by the
11 20
   :ref:`ubuntu_linux` installation path. It is the one that most developers
12 21
   will use, and the one that receives the most attention from the core
13 22
   contributors. If you decide to go with a different kernel and hit a bug,
14 23
   please try to reproduce it with the official kernels first.
15 24
 
16
-If for some reason you cannot or do not want to use the "official" kernels,
25
+If you cannot or do not want to use the "official" kernels,
17 26
 here is some technical background about the features (both optional and
18 27
 mandatory) that docker needs to run successfully.
19 28
 
20
-In short, you need kernel version 3.8 (or above), compiled to include
21
-`AUFS support <http://aufs.sourceforge.net/>`_. Of course, you need to
22
-enable cgroups and namespaces.
23
-
24
-
25
-Namespaces and Cgroups
26
-
27
-You need to enable namespaces and cgroups, to the extend of what is needed
28
-to run LXC containers. Technically, while namespaces have been introduced
29
-in the early 2.6 kernels, we do not advise to try any kernel before 2.6.32
30
-to run LXC containers. Note that 2.6.32 has some documented issues regarding
31
-network namespace setup and teardown; those issues are not a risk if you
32
-run containers in a private environment, but can lead to denial-of-service
33
-attacks if you want to run untrusted code in your containers. For more details,
34
-see `[LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
35
-
36
-Kernels 2.6.38, and every version since 3.2, have been deployed successfully
37
-to run containerized production workloads. Feature-wise, there is no huge
38
-improvement between 2.6.38 and up to 3.6 (as far as docker is concerned!).
39
-
40
-Starting with version 3.7, the kernel has basic support for
41
-`Checkpoint/Restore In Userspace <http://criu.org/>`_, which is not used by
42
-docker at this point, but allows to suspend the state of a container to
43
-disk and resume it later.
44
-
45
-Version 3.8 provides improvements in stability, which are deemed necessary
46
-for the operation of docker. Versions 3.2 to 3.5 have been shown to
47
-exhibit a reproducible bug (for more details, see issue
48
-`#407 <https://github.com/dotcloud/docker/issues/407>`_).
49
-
50
-Version 3.8 also brings better support for the
51
-`setns() syscall <http://lwn.net/Articles/531381/>`_ -- but this should not
52
-be a concern since docker does not leverage on this feature for now.
53
-
54
-If you want a technical overview about those concepts, you might
55
-want to check those articles on dotCloud's blog:
56
-`about namespaces <http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part>`_
57
-and `about cgroups <http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-24-c>`_.
58
-
59
-
60
-Important Note About Pre-3.8 Kernels
29
+Linux version 3.8 or above
30
+--------------------------
61 31
 
62
-As mentioned above, kernels before 3.8 are not stable when used with docker.
32
+Kernel versions 3.2 to 3.5 are not stable when used with docker.
63 33
 In some circumstances, you will experience kernel "oopses", or even crashes.
64 34
 The symptoms include:
65 35
 
... ...
@@ -81,6 +49,36 @@ detects something older than 3.8.
81 81
 See issue `#407 <https://github.com/dotcloud/docker/issues/407>`_ for details.
82 82
 
83 83
 
84
+AUFS support
85
+------------
86
+
87
+Docker currently relies on AUFS, an unioning filesystem.
88
+While AUFS is included in the kernels built by the Debian and Ubuntu
89
+distributions, is not part of the standard kernel. This means that if
90
+you decide to roll your own kernel, you will have to patch your
91
+kernel tree to add AUFS. The process is documented on
92
+`AUFS webpage <http://aufs.sourceforge.net/>`_.
93
+
94
+
95
+Cgroups and namespaces
96
+----------------------
97
+
98
+You need to enable namespaces and cgroups, to the extend of what is needed
99
+to run LXC containers. Technically, while namespaces have been introduced
100
+in the early 2.6 kernels, we do not advise to try any kernel before 2.6.32
101
+to run LXC containers. Note that 2.6.32 has some documented issues regarding
102
+network namespace setup and teardown; those issues are not a risk if you
103
+run containers in a private environment, but can lead to denial-of-service
104
+attacks if you want to run untrusted code in your containers. For more details,
105
+see `[LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
106
+
107
+Kernels 2.6.38, and every version since 3.2, have been deployed successfully
108
+to run containerized production workloads. Feature-wise, there is no huge
109
+improvement between 2.6.38 and up to 3.6 (as far as docker is concerned!).
110
+
111
+
112
+
113
+
84 114
 Extra Cgroup Controllers
85 115
 ------------------------
86 116
 
... ...
@@ -115,39 +113,3 @@ And replace it by the following one::
115 115
     GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount"
116 116
 
117 117
 Then run ``update-grub``, and reboot.
118
-
119
-
120
-AUFS
121
-
122
-Docker currently relies on AUFS, an unioning filesystem.
123
-While AUFS is included in the kernels built by the Debian and Ubuntu
124
-distributions, is not part of the standard kernel. This means that if
125
-you decide to roll your own kernel, you will have to patch your
126
-kernel tree to add AUFS. The process is documented on
127
-`AUFS webpage <http://aufs.sourceforge.net/>`_.
128
-
129
-Note: the AUFS patch is fairly intrusive, but for the record, people have
130
-successfully applied GRSEC and AUFS together, to obtain hardened production
131
-kernels.
132
-
133
-If you want more information about that topic, there is an
134
-`article about AUFS on dotCloud's blog 
135
-<http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-34-a>`_.
136
-
137
-
138
-BTRFS, ZFS, OverlayFS...
139
-
140
-There is ongoing development on docker, to implement support for
141
-`BTRFS <http://en.wikipedia.org/wiki/Btrfs>`_
142
-(see github issue `#443 <https://github.com/dotcloud/docker/issues/443>`_).
143
-
144
-People have also showed interest for `ZFS <http://en.wikipedia.org/wiki/ZFS>`_
145
-(using e.g. `ZFS-on-Linux <http://zfsonlinux.org/>`_) and OverlayFS.
146
-The latter is functionally close to AUFS, and it might end up being included
147
-in the stock kernel; so it's a strong candidate!
148
-
149
-Would you like to `contribute
150
-<https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`_
151
-support for your favorite filesystem?
... ...
@@ -198,6 +198,35 @@
198 198
 
199 199
 
200 200
 <div class="container">
201
+
202
+    <div class="row">
203
+        <div class="span6">
204
+            <section class="contentblock twitterblock">
205
+                <img src="https://si0.twimg.com/profile_images/2707460527/252a64411a339184ff375a96fb68dcb0_bigger.png">
206
+                <em>Mitchell Hashimoto‏@mitchellh:</em> Docker launched today. It is incredible. They’re also working RIGHT NOW on a Vagrant provider. LXC is COMING!!
207
+            </section>
208
+        </div>
209
+        <div class="span6">
210
+            <section class="contentblock twitterblock">
211
+                <img src="https://si0.twimg.com/profile_images/1108290260/Adam_Jacob-114x150_original_bigger.jpg">
212
+                <em>Adam Jacob‏@adamhjk:</em> Docker is clearly the right idea. @solomonstre absolutely killed it. Containerized app deployment is the future, I think.
213
+            </section>
214
+        </div>
215
+    </div>
216
+    <div class="row">
217
+        <div class="span6">
218
+            <section class="contentblock twitterblock">
219
+                <img src="https://si0.twimg.com/profile_images/14872832/twitter_pic_bigger.jpg">
220
+                <em>Matt Townsend‏@mtownsend:</em> I have a serious code crush on docker.io - it's Lego for PaaS. Motherfucking awesome Lego.
221
+            </section>
222
+        </div>
223
+        <div class="span6">
224
+            <section class="contentblock twitterblock">
225
+                <img src="https://si0.twimg.com/profile_images/1312352395/rupert-259x300_bigger.jpg">
226
+                <em>Rob Harrop‏@robertharrop:</em> Impressed by @getdocker - it's all kinds of magic. Serious rethink of AWS architecture happening @skillsmatter.
227
+            </section>
228
+        </div>
229
+    </div>
201 230
     <div class="row">
202 231
         <div class="span6">
203 232
             <section class="contentblock twitterblock">
... ...
@@ -165,7 +165,7 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output
165 165
 	if err != nil {
166 166
 		return nil, err
167 167
 	}
168
-	return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)"), tmp.Root)
168
+	return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)", false), tmp.Root)
169 169
 }
170 170
 
171 171
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.
... ...
@@ -1,37 +1,44 @@
1
+lxc-docker (0.3.3-1) precise; urgency=low
2
+  - Registry: Fix push regression
3
+  - Various bugfixes
4
+
5
+ -- dotCloud <ops@dotcloud.com>  Thu, 23 May 2013 00:00:00 -0700
6
+
7
+
1 8
 lxc-docker (0.3.2-1) precise; urgency=low
2
- - Runtime: Store the actual archive on commit
3
- - Registry: Improve the checksum process
4
- - Registry: Use the size to have a good progress bar while pushing
5
- - Registry: Use the actual archive if it exists in order to speed up the push
6
- - Registry: Fix error 400 on push
9
+  - Runtime: Store the actual archive on commit
10
+  - Registry: Improve the checksum process
11
+  - Registry: Use the size to have a good progress bar while pushing
12
+  - Registry: Use the actual archive if it exists in order to speed up the push
13
+  - Registry: Fix error 400 on push
7 14
 
8 15
  -- dotCloud <ops@dotcloud.com>  Fri, 9 May 2013 00:00:00 -0700
9 16
 
10 17
 
11 18
 lxc-docker (0.3.1-1) precise; urgency=low
12
- - Builder: Implement the autorun capability within docker builder
13
- - Builder: Add caching to docker builder
14
- - Builder: Add support for docker builder with native API as top level command
15
- - Runtime: Add go version to debug infos
16
- - Builder: Implement ENV within docker builder
17
- - Registry: Add docker search top level command in order to search a repository
18
- - Images: output graph of images to dot (graphviz)
19
- - Documentation: new introduction and high-level overview
20
- - Documentation: Add the documentation for docker builder
21
- - Website: new high-level overview
22
- - Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc
23
- - Images: fix ByParent function
24
- - Builder: Check the command existance prior create and add Unit tests for the case
25
- - Registry: Fix pull for official images with specific tag
26
- - Registry: Fix issue when login in with a different user and trying to push
27
- - Documentation: CSS fix for docker documentation to make REST API docs look better.
28
- - Documentation: Fixed CouchDB example page header mistake
29
- - Documentation: fixed README formatting
30
- - Registry: Improve checksum - async calculation
31
- - Runtime: kernel version - don't show the dash if flavor is empty
32
- - Documentation: updated www.docker.io website.
33
- - Builder: use any whitespaces instead of tabs
34
- - Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker
19
+  - Builder: Implement the autorun capability within docker builder
20
+  - Builder: Add caching to docker builder
21
+  - Builder: Add support for docker builder with native API as top level command
22
+  - Runtime: Add go version to debug infos
23
+  - Builder: Implement ENV within docker builder
24
+  - Registry: Add docker search top level command in order to search a repository
25
+  - Images: output graph of images to dot (graphviz)
26
+  - Documentation: new introduction and high-level overview
27
+  - Documentation: Add the documentation for docker builder
28
+  - Website: new high-level overview
29
+  - Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc
30
+  - Images: fix ByParent function
31
+  - Builder: Check the command existance prior create and add Unit tests for the case
32
+  - Registry: Fix pull for official images with specific tag
33
+  - Registry: Fix issue when login in with a different user and trying to push
34
+  - Documentation: CSS fix for docker documentation to make REST API docs look better.
35
+  - Documentation: Fixed CouchDB example page header mistake
36
+  - Documentation: fixed README formatting
37
+  - Registry: Improve checksum - async calculation
38
+  - Runtime: kernel version - don't show the dash if flavor is empty
39
+  - Documentation: updated www.docker.io website.
40
+  - Builder: use any whitespaces instead of tabs
41
+  - Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker
35 42
 
36 43
  -- dotCloud <ops@dotcloud.com>  Fri, 8 May 2013 00:00:00 -0700
37 44
 
... ...
@@ -428,9 +428,14 @@ func (r *Registry) ResetClient(authConfig *auth.AuthConfig) {
428 428
 	r.client.Jar = cookiejar.NewCookieJar()
429 429
 }
430 430
 
431
-func (r *Registry) GetAuthConfig() *auth.AuthConfig {
431
+func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig {
432
+	password := ""
433
+	if withPasswd {
434
+		password = r.authConfig.Password
435
+	}
432 436
 	return &auth.AuthConfig{
433 437
 		Username: r.authConfig.Username,
438
+		Password: password,
434 439
 		Email:    r.authConfig.Email,
435 440
 	}
436 441
 }
... ...
@@ -91,7 +91,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error {
91 91
 		return err
92 92
 	}
93 93
 
94
-	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil {
94
+	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)\r", false), path); err != nil {
95 95
 		return err
96 96
 	}
97 97
 	// FIXME: Handle custom repo, tag comment, author
... ...
@@ -291,16 +291,17 @@ func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
291 291
 	return nil
292 292
 }
293 293
 
294
-func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string) error {
294
+func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string, json bool) error {
295 295
 	history, err := srv.registry.GetRemoteHistory(imgId, registry, token)
296 296
 	if err != nil {
297 297
 		return err
298 298
 	}
299
+
299 300
 	// FIXME: Try to stream the images?
300 301
 	// FIXME: Launch the getRemoteImage() in goroutines
301 302
 	for _, id := range history {
302 303
 		if !srv.runtime.graph.Exists(id) {
303
-			fmt.Fprintf(out, "{\"status\" :\"Pulling %s metadata\"}", id)
304
+			fmt.Fprintf(out, utils.FormatStatus("Pulling %s metadata", json), id)
304 305
 			imgJson, err := srv.registry.GetRemoteImageJson(id, registry, token)
305 306
 			if err != nil {
306 307
 				// FIXME: Keep goging in case of error?
... ...
@@ -312,12 +313,12 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
312 312
 			}
313 313
 
314 314
 			// Get the layer
315
-			fmt.Fprintf(out, "{\"status\" :\"Pulling %s fs layer\"}", img.Id)
315
+			fmt.Fprintf(out, utils.FormatStatus("Pulling %s fs layer", json), id)
316 316
 			layer, contentLength, err := srv.registry.GetRemoteImageLayer(img.Id, registry, token)
317 317
 			if err != nil {
318 318
 				return err
319 319
 			}
320
-			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, ""), false, img); err != nil {
320
+			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, utils.FormatProgress("%v/%v (%v)", json), json), false, img); err != nil {
321 321
 				return err
322 322
 			}
323 323
 		}
... ...
@@ -325,8 +326,8 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
325 325
 	return nil
326 326
 }
327 327
 
328
-func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error {
329
-	fmt.Fprintf(out, "{\"status\":\"Pulling repository %s from %s\"}", remote, auth.IndexServerAddress())
328
+func (srv *Server) pullRepository(out io.Writer, remote, askedTag string, json bool) error {
329
+	fmt.Fprintf(out, utils.FormatStatus("Pulling repository %s from %s", json), remote, auth.IndexServerAddress())
330 330
 	repoData, err := srv.registry.GetRepositoryData(remote)
331 331
 	if err != nil {
332 332
 		return err
... ...
@@ -363,11 +364,11 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error
363 363
 			utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.Id)
364 364
 			continue
365 365
 		}
366
-		fmt.Fprintf(out, "{\"status\":\"Pulling image %s (%s) from %s\"}", img.Id, img.Tag, remote)
366
+		fmt.Fprintf(out, utils.FormatStatus("Pulling image %s (%s) from %s", json), img.Id, img.Tag, remote)
367 367
 		success := false
368 368
 		for _, ep := range repoData.Endpoints {
369
-			if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil {
370
-				fmt.Fprintf(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err)
369
+			if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens, json); err != nil {
370
+				fmt.Fprintf(out, utils.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint\n", json), askedTag, err)
371 371
 				continue
372 372
 			}
373 373
 			success = true
... ...
@@ -392,18 +393,19 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error
392 392
 	return nil
393 393
 }
394 394
 
395
-func (srv *Server) ImagePull(name, tag, registry string, out io.Writer) error {
395
+func (srv *Server) ImagePull(name, tag, registry string, out io.Writer, json bool) error {
396 396
 	out = utils.NewWriteFlusher(out)
397 397
 	if registry != "" {
398
-		if err := srv.pullImage(out, name, registry, nil); err != nil {
398
+		if err := srv.pullImage(out, name, registry, nil, json); err != nil {
399 399
 			return err
400 400
 		}
401 401
 		return nil
402 402
 	}
403 403
 
404
-	if err := srv.pullRepository(out, name, tag); err != nil {
404
+	if err := srv.pullRepository(out, name, tag, json); err != nil {
405 405
 		return err
406 406
 	}
407
+
407 408
 	return nil
408 409
 }
409 410
 
... ...
@@ -567,7 +569,7 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st
567 567
 	}
568 568
 
569 569
 	// Send the layer
570
-	if err := srv.registry.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, ""), ep, token); err != nil {
570
+	if err := srv.registry.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, "", false), ep, token); err != nil {
571 571
 		return err
572 572
 	}
573 573
 	return nil
... ...
@@ -618,7 +620,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
618 618
 		if err != nil {
619 619
 			return err
620 620
 		}
621
-		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)")
621
+		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)\r", false)
622 622
 	}
623 623
 	img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
624 624
 	if err != nil {
... ...
@@ -49,10 +49,10 @@ func CompareConfig(a, b *Config) bool {
49 49
 }
50 50
 
51 51
 func MergeConfig(userConf, imageConf *Config) {
52
-	if userConf.Hostname != "" {
52
+	if userConf.Hostname == "" {
53 53
 		userConf.Hostname = imageConf.Hostname
54 54
 	}
55
-	if userConf.User != "" {
55
+	if userConf.User == "" {
56 56
 		userConf.User = imageConf.User
57 57
 	}
58 58
 	if userConf.Memory == 0 {
... ...
@@ -69,6 +69,7 @@ type progressReader struct {
69 69
 	readProgress int           // How much has been read so far (bytes)
70 70
 	lastUpdate   int           // How many bytes read at least update
71 71
 	template     string        // Template to print. Default "%v/%v (%v)"
72
+	json bool
72 73
 }
73 74
 
74 75
 func (r *progressReader) Read(p []byte) (n int, err error) {
... ...
@@ -84,22 +85,27 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
84 84
 	}
85 85
 	if r.readProgress-r.lastUpdate > updateEvery || err != nil {
86 86
 		if r.readTotal > 0 {
87
-			fmt.Fprintf(r.output, r.template, r.readProgress, r.readTotal)
87
+			fmt.Fprintf(r.output, r.template, r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
88 88
 		} else {
89
-			fmt.Fprintf(r.output, r.template, r.readProgress, "?")
89
+			fmt.Fprintf(r.output, r.template, r.readProgress, "?", "n/a")
90 90
 		}
91 91
 		r.lastUpdate = r.readProgress
92 92
 	}
93
+	// Send newline when complete
94
+	if err != nil {
95
+		fmt.Fprintf(r.output, FormatStatus("", r.json))
96
+	}
97
+
93 98
 	return read, err
94 99
 }
95 100
 func (r *progressReader) Close() error {
96 101
 	return io.ReadCloser(r.reader).Close()
97 102
 }
98
-func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string) *progressReader {
99
-	if template == "" {
100
-		template = "{\"progress\":\"%v/%v\"}"
103
+func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string, json bool) *progressReader {
104
+      	if template == "" {
105
+		template = "%v/%v (%v)\r"
101 106
 	}
102
-	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template}
107
+	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template, json}
103 108
 }
104 109
 
105 110
 // HumanDuration returns a human-readable approximation of a duration
... ...
@@ -550,3 +556,19 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
550 550
 	}
551 551
 	return &WriteFlusher{w: w, flusher: flusher}
552 552
 }
553
+
554
+func FormatStatus(str string, json bool) string {
555
+	if json {
556
+		return "{\"status\" : \"" + str + "\"}"
557
+	}
558
+	return str + "\r\n"
559
+}
560
+
561
+func FormatProgress(str string, json bool) string {
562
+	if json {
563
+		return "{\"progress\" : \"" + str + "\"}"
564
+	}
565
+	return "Downloading " + str + "\r"
566
+}
567
+
568
+