Browse code

Merge master

Guillaume J. Charmes authored on 2013/05/29 05:42:50
Showing 73 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
... ...
@@ -12,7 +12,7 @@ Docker is an open-source implementation of the deployment engine which powers [d
12 12
 It benefits directly from the experience accumulated over several years of large-scale operation and support of hundreds of thousands
13 13
 of applications and databases.
14 14
 
15
-![Docker L](docs/sources/static_files/lego_docker.jpg "Docker")
15
+![Docker L](docs/sources/concepts/images/lego_docker.jpg "Docker")
16 16
 
17 17
 ## Better than VMs
18 18
 
... ...
@@ -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 {
... ...
@@ -53,6 +55,7 @@ func writeJson(w http.ResponseWriter, b []byte) {
53 53
 	w.Write(b)
54 54
 }
55 55
 
56
+// FIXME: Use stvconv.ParseBool() instead?
56 57
 func getBoolParam(value string) (bool, error) {
57 58
 	if value == "1" || strings.ToLower(value) == "true" {
58 59
 		return true, nil
... ...
@@ -63,8 +66,8 @@ func getBoolParam(value string) (bool, error) {
63 63
 	return false, fmt.Errorf("Bad parameter")
64 64
 }
65 65
 
66
-func getAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
67
-	b, err := json.Marshal(srv.registry.GetAuthConfig())
66
+func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
67
+	b, err := json.Marshal(srv.registry.GetAuthConfig(false))
68 68
 	if err != nil {
69 69
 		return err
70 70
 	}
... ...
@@ -72,14 +75,14 @@ func getAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[strin
72 72
 	return nil
73 73
 }
74 74
 
75
-func postAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
75
+func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
76 76
 	config := &auth.AuthConfig{}
77 77
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
78 78
 		return err
79 79
 	}
80
-
81
-	if config.Username == srv.registry.GetAuthConfig().Username {
82
-		config.Password = srv.registry.GetAuthConfig().Password
80
+	authConfig := srv.registry.GetAuthConfig(true)
81
+	if config.Username == authConfig.Username {
82
+		config.Password = authConfig.Password
83 83
 	}
84 84
 
85 85
 	newAuthConfig := auth.NewAuthConfig(config.Username, config.Password, config.Email, srv.runtime.root)
... ...
@@ -101,7 +104,7 @@ func postAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[stri
101 101
 	return nil
102 102
 }
103 103
 
104
-func getVersion(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
104
+func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
105 105
 	m := srv.DockerVersion()
106 106
 	b, err := json.Marshal(m)
107 107
 	if err != nil {
... ...
@@ -111,7 +114,7 @@ func getVersion(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st
111 111
 	return nil
112 112
 }
113 113
 
114
-func postContainersKill(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
114
+func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
115 115
 	if vars == nil {
116 116
 		return fmt.Errorf("Missing parameter")
117 117
 	}
... ...
@@ -123,7 +126,7 @@ func postContainersKill(srv *Server, w http.ResponseWriter, r *http.Request, var
123 123
 	return nil
124 124
 }
125 125
 
126
-func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
126
+func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
127 127
 	if vars == nil {
128 128
 		return fmt.Errorf("Missing parameter")
129 129
 	}
... ...
@@ -136,7 +139,7 @@ func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, va
136 136
 	return nil
137 137
 }
138 138
 
139
-func getImagesJson(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
139
+func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
140 140
 	if err := parseForm(r); err != nil {
141 141
 		return err
142 142
 	}
... ...
@@ -159,14 +162,14 @@ func getImagesJson(srv *Server, w http.ResponseWriter, r *http.Request, vars map
159 159
 	return nil
160 160
 }
161 161
 
162
-func getImagesViz(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
162
+func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
163 163
 	if err := srv.ImagesViz(w); err != nil {
164 164
 		return err
165 165
 	}
166 166
 	return nil
167 167
 }
168 168
 
169
-func getInfo(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
169
+func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
170 170
 	out := srv.DockerInfo()
171 171
 	b, err := json.Marshal(out)
172 172
 	if err != nil {
... ...
@@ -176,7 +179,7 @@ func getInfo(srv *Server, w http.ResponseWriter, r *http.Request, vars map[strin
176 176
 	return nil
177 177
 }
178 178
 
179
-func getImagesHistory(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
179
+func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
180 180
 	if vars == nil {
181 181
 		return fmt.Errorf("Missing parameter")
182 182
 	}
... ...
@@ -193,7 +196,7 @@ func getImagesHistory(srv *Server, w http.ResponseWriter, r *http.Request, vars
193 193
 	return nil
194 194
 }
195 195
 
196
-func getContainersChanges(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
196
+func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
197 197
 	if vars == nil {
198 198
 		return fmt.Errorf("Missing parameter")
199 199
 	}
... ...
@@ -210,7 +213,7 @@ func getContainersChanges(srv *Server, w http.ResponseWriter, r *http.Request, v
210 210
 	return nil
211 211
 }
212 212
 
213
-func getContainersPs(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
213
+func getContainersPs(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
214 214
 	if err := parseForm(r); err != nil {
215 215
 		return err
216 216
 	}
... ...
@@ -234,7 +237,7 @@ func getContainersPs(srv *Server, w http.ResponseWriter, r *http.Request, vars m
234 234
 	return nil
235 235
 }
236 236
 
237
-func postImagesTag(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
237
+func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
238 238
 	if err := parseForm(r); err != nil {
239 239
 		return err
240 240
 	}
... ...
@@ -256,7 +259,7 @@ func postImagesTag(srv *Server, w http.ResponseWriter, r *http.Request, vars map
256 256
 	return nil
257 257
 }
258 258
 
259
-func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
259
+func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
260 260
 	if err := parseForm(r); err != nil {
261 261
 		return err
262 262
 	}
... ...
@@ -283,7 +286,7 @@ func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st
283 283
 }
284 284
 
285 285
 // Creates an image from Pull or from Import
286
-func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
286
+func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
287 287
 	if err := parseForm(r); err != nil {
288 288
 		return err
289 289
 	}
... ...
@@ -295,7 +298,10 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars
295 295
 
296 296
 	if image != "" { //pull
297 297
 		registry := r.Form.Get("registry")
298
-		if err := srv.ImagePull(image, tag, registry, w); err != nil {
298
+		if version > 1.0 {
299
+			w.Header().Set("Content-Type", "application/json")
300
+		}
301
+		if err := srv.ImagePull(image, tag, registry, w, version > 1.0); err != nil {
299 302
 			return err
300 303
 		}
301 304
 	} else { //import
... ...
@@ -306,7 +312,7 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars
306 306
 	return nil
307 307
 }
308 308
 
309
-func getImagesSearch(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
309
+func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
310 310
 	if err := parseForm(r); err != nil {
311 311
 		return err
312 312
 	}
... ...
@@ -324,7 +330,7 @@ func getImagesSearch(srv *Server, w http.ResponseWriter, r *http.Request, vars m
324 324
 	return nil
325 325
 }
326 326
 
327
-func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
327
+func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
328 328
 	if err := parseForm(r); err != nil {
329 329
 		return err
330 330
 	}
... ...
@@ -348,7 +354,7 @@ func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars
348 348
 	return nil
349 349
 }
350 350
 
351
-func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
351
+func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
352 352
 	if err := parseForm(r); err != nil {
353 353
 		return err
354 354
 	}
... ...
@@ -365,7 +371,7 @@ func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars ma
365 365
 	return nil
366 366
 }
367 367
 
368
-func postContainersCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
368
+func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
369 369
 	config := &Config{}
370 370
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
371 371
 		return err
... ...
@@ -395,7 +401,7 @@ func postContainersCreate(srv *Server, w http.ResponseWriter, r *http.Request, v
395 395
 	return nil
396 396
 }
397 397
 
398
-func postContainersRestart(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
398
+func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
399 399
 	if err := parseForm(r); err != nil {
400 400
 		return err
401 401
 	}
... ...
@@ -414,7 +420,7 @@ func postContainersRestart(srv *Server, w http.ResponseWriter, r *http.Request,
414 414
 	return nil
415 415
 }
416 416
 
417
-func deleteContainers(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
417
+func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
418 418
 	if err := parseForm(r); err != nil {
419 419
 		return err
420 420
 	}
... ...
@@ -434,7 +440,7 @@ func deleteContainers(srv *Server, w http.ResponseWriter, r *http.Request, vars
434 434
 	return nil
435 435
 }
436 436
 
437
-func deleteImages(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
437
+func deleteImages(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 +452,7 @@ func deleteImages(srv *Server, w http.ResponseWriter, r *http.Request, vars map[
446 446
 	return nil
447 447
 }
448 448
 
449
-func postContainersStart(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
449
+func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
450 450
 	if vars == nil {
451 451
 		return fmt.Errorf("Missing parameter")
452 452
 	}
... ...
@@ -458,7 +464,7 @@ func postContainersStart(srv *Server, w http.ResponseWriter, r *http.Request, va
458 458
 	return nil
459 459
 }
460 460
 
461
-func postContainersStop(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
461
+func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
462 462
 	if err := parseForm(r); err != nil {
463 463
 		return err
464 464
 	}
... ...
@@ -479,7 +485,7 @@ func postContainersStop(srv *Server, w http.ResponseWriter, r *http.Request, var
479 479
 	return nil
480 480
 }
481 481
 
482
-func postContainersWait(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
482
+func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
483 483
 	if vars == nil {
484 484
 		return fmt.Errorf("Missing parameter")
485 485
 	}
... ...
@@ -496,7 +502,29 @@ func postContainersWait(srv *Server, w http.ResponseWriter, r *http.Request, var
496 496
 	return nil
497 497
 }
498 498
 
499
-func postContainersAttach(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
499
+func postContainersResize(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
500
+	if err := parseForm(r); err != nil {
501
+		return err
502
+	}
503
+	height, err := strconv.Atoi(r.Form.Get("h"))
504
+	if err != nil {
505
+		return err
506
+	}
507
+	width, err := strconv.Atoi(r.Form.Get("w"))
508
+	if err != nil {
509
+		return err
510
+	}
511
+	if vars == nil {
512
+		return fmt.Errorf("Missing parameter")
513
+	}
514
+	name := vars["name"]
515
+	if err := srv.ContainerResize(name, height, width); err != nil {
516
+		return err
517
+	}
518
+	return nil
519
+}
520
+
521
+func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
500 522
 	if err := parseForm(r); err != nil {
501 523
 		return err
502 524
 	}
... ...
@@ -539,7 +567,7 @@ func postContainersAttach(srv *Server, w http.ResponseWriter, r *http.Request, v
539 539
 	return nil
540 540
 }
541 541
 
542
-func getContainersByName(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
542
+func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
543 543
 	if vars == nil {
544 544
 		return fmt.Errorf("Missing parameter")
545 545
 	}
... ...
@@ -557,7 +585,7 @@ func getContainersByName(srv *Server, w http.ResponseWriter, r *http.Request, va
557 557
 	return nil
558 558
 }
559 559
 
560
-func getImagesByName(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
560
+func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
561 561
 	if vars == nil {
562 562
 		return fmt.Errorf("Missing parameter")
563 563
 	}
... ...
@@ -575,7 +603,7 @@ func getImagesByName(srv *Server, w http.ResponseWriter, r *http.Request, vars m
575 575
 	return nil
576 576
 }
577 577
 
578
-func postImagesGetCache(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
578
+func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
579 579
 	apiConfig := &ApiImageConfig{}
580 580
 	if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
581 581
 		return err
... ...
@@ -626,7 +654,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
626 626
 	r := mux.NewRouter()
627 627
 	log.Printf("Listening for HTTP on %s\n", addr)
628 628
 
629
-	m := map[string]map[string]func(*Server, http.ResponseWriter, *http.Request, map[string]string) error{
629
+	m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{
630 630
 		"GET": {
631 631
 			"/auth":                         getAuth,
632 632
 			"/version":                      getVersion,
... ...
@@ -656,6 +684,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
656 656
 			"/containers/{name:.*}/start":   postContainersStart,
657 657
 			"/containers/{name:.*}/stop":    postContainersStop,
658 658
 			"/containers/{name:.*}/wait":    postContainersWait,
659
+			"/containers/{name:.*}/resize":  postContainersResize,
659 660
 			"/containers/{name:.*}/attach":  postContainersAttach,
660 661
 		},
661 662
 		"DELETE": {
... ...
@@ -671,7 +700,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
671 671
 			localRoute := route
672 672
 			localMethod := method
673 673
 			localFct := fct
674
-			r.Path(localRoute).Methods(localMethod).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
674
+			f := func(w http.ResponseWriter, r *http.Request) {
675 675
 				utils.Debugf("Calling %s %s", localMethod, localRoute)
676 676
 				if logging {
677 677
 					log.Println(r.Method, r.RequestURI)
... ...
@@ -682,12 +711,21 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
682 682
 						utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
683 683
 					}
684 684
 				}
685
-				if err := localFct(srv, w, r, mux.Vars(r)); err != nil {
685
+				version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
686
+				if err != nil {
687
+					version = API_VERSION
688
+				}
689
+				if version == 0 || version > API_VERSION {
690
+					w.WriteHeader(http.StatusNotFound)
691
+					return
692
+				}
693
+				if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil {
686 694
 					httpError(w, err)
687 695
 				}
688
-			})
696
+			}
697
+			r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
698
+			r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
689 699
 		}
690 700
 	}
691
-
692 701
 	return http.ListenAndServe(addr, r)
693 702
 }
... ...
@@ -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
 
... ...
@@ -56,7 +56,7 @@ func TestGetAuth(t *testing.T) {
56 56
 		t.Fatalf("%d OK or 0 expected, received %d\n", http.StatusOK, r.Code)
57 57
 	}
58 58
 
59
-	newAuthConfig := srv.registry.GetAuthConfig()
59
+	newAuthConfig := srv.registry.GetAuthConfig(false)
60 60
 	if newAuthConfig.Username != authConfig.Username ||
61 61
 		newAuthConfig.Email != authConfig.Email {
62 62
 		t.Fatalf("The auth configuration hasn't been set correctly")
... ...
@@ -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 {
... ...
@@ -26,12 +26,6 @@ type builderClient struct {
26 26
 }
27 27
 
28 28
 func (b *builderClient) clearTmp(containers, images map[string]struct{}) {
29
-	for c := range containers {
30
-		if _, _, err := b.cli.call("DELETE", "/containers/"+c, nil); err != nil {
31
-			utils.Debugf("%s", err)
32
-		}
33
-		utils.Debugf("Removing container %s", c)
34
-	}
35 29
 	for i := range images {
36 30
 		if _, _, err := b.cli.call("DELETE", "/images/"+i, nil); err != nil {
37 31
 			utils.Debugf("%s", err)
... ...
@@ -16,30 +16,36 @@ import (
16 16
 	"net/http/httputil"
17 17
 	"net/url"
18 18
 	"os"
19
+	"os/signal"
19 20
 	"path/filepath"
20 21
 	"reflect"
21 22
 	"strconv"
22 23
 	"strings"
24
+	"syscall"
23 25
 	"text/tabwriter"
24 26
 	"time"
25 27
 	"unicode"
26 28
 )
27 29
 
28
-const VERSION = "0.3.2"
30
+const VERSION = "0.3.3"
29 31
 
30 32
 var (
31 33
 	GIT_COMMIT string
32 34
 )
33 35
 
34
-func ParseCommands(args ...string) error {
35
-	cli := NewDockerCli("0.0.0.0", 4243)
36
+func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
37
+	methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
38
+	return reflect.TypeOf(cli).MethodByName(methodName)
39
+}
40
+
41
+func ParseCommands(addr string, port int, args ...string) error {
42
+	cli := NewDockerCli(addr, port)
36 43
 
37 44
 	if len(args) > 0 {
38
-		methodName := "Cmd" + strings.ToUpper(args[0][:1]) + strings.ToLower(args[0][1:])
39
-		method, exists := reflect.TypeOf(cli).MethodByName(methodName)
45
+		method, exists := cli.getMethod(args[0])
40 46
 		if !exists {
41 47
 			fmt.Println("Error: Command not found:", args[0])
42
-			return cli.CmdHelp(args...)
48
+			return cli.CmdHelp(args[1:]...)
43 49
 		}
44 50
 		ret := method.Func.CallSlice([]reflect.Value{
45 51
 			reflect.ValueOf(cli),
... ...
@@ -54,7 +60,19 @@ func ParseCommands(args ...string) error {
54 54
 }
55 55
 
56 56
 func (cli *DockerCli) CmdHelp(args ...string) error {
57
-	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
57
+	if len(args) > 0 {
58
+		method, exists := cli.getMethod(args[0])
59
+		if !exists {
60
+			fmt.Println("Error: Command not found:", args[0])
61
+		} else {
62
+			method.Func.CallSlice([]reflect.Value{
63
+				reflect.ValueOf(cli),
64
+				reflect.ValueOf([]string{"--help"}),
65
+			})[0].Interface()
66
+			return nil
67
+		}
68
+	}
69
+	help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n  -H=\"%s:%d\": Host:port to bind/connect to\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", cli.host, cli.port)
58 70
 	for cmd, description := range map[string]string{
59 71
 		"attach":  "Attach to a running container",
60 72
 		"build":   "Build a container from a Dockerfile",
... ...
@@ -660,39 +678,13 @@ func (cli *DockerCli) CmdPush(args ...string) error {
660 660
 		return nil
661 661
 	}
662 662
 
663
-	body, _, err := cli.call("GET", "/auth", nil)
663
+	username, err := cli.checkIfLogged(*registry == "", "push")
664 664
 	if err != nil {
665 665
 		return err
666 666
 	}
667 667
 
668
-	var out auth.AuthConfig
669
-	err = json.Unmarshal(body, &out)
670
-	if err != nil {
671
-		return err
672
-	}
673
-
674
-	// If the login failed AND we're using the index, abort
675
-	if *registry == "" && out.Username == "" {
676
-		if err := cli.CmdLogin(args...); err != nil {
677
-			return err
678
-		}
679
-
680
-		body, _, err = cli.call("GET", "/auth", nil)
681
-		if err != nil {
682
-			return err
683
-		}
684
-		err = json.Unmarshal(body, &out)
685
-		if err != nil {
686
-			return err
687
-		}
688
-
689
-		if out.Username == "" {
690
-			return fmt.Errorf("Please login prior to push. ('docker login')")
691
-		}
692
-	}
693
-
694 668
 	if len(strings.SplitN(name, "/", 2)) == 1 {
695
-		return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", out.Username, name)
669
+		return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name)
696 670
 	}
697 671
 
698 672
 	v := url.Values{}
... ...
@@ -723,6 +715,12 @@ func (cli *DockerCli) CmdPull(args ...string) error {
723 723
 		remote = remoteParts[0]
724 724
 	}
725 725
 
726
+	if strings.Contains(remote, "/") {
727
+		if _, err := cli.checkIfLogged(true, "pull"); err != nil {
728
+			return err
729
+		}
730
+	}
731
+
726 732
 	v := url.Values{}
727 733
 	v.Set("fromImage", remote)
728 734
 	v.Set("tag", *tag)
... ...
@@ -1013,6 +1011,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
1013 1013
 	v.Set("stderr", "1")
1014 1014
 	v.Set("stdin", "1")
1015 1015
 
1016
+	cli.monitorTtySize(cmd.Arg(0))
1016 1017
 	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty); err != nil {
1017 1018
 		return err
1018 1019
 	}
... ...
@@ -1200,6 +1199,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1200 1200
 	}
1201 1201
 
1202 1202
 	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
1203
+		cli.monitorTtySize(out.Id)
1203 1204
 		if err := cli.hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty); err != nil {
1204 1205
 			return err
1205 1206
 		}
... ...
@@ -1210,6 +1210,40 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1210 1210
 	return nil
1211 1211
 }
1212 1212
 
1213
+func (cli *DockerCli) checkIfLogged(condition bool, action string) (string, error) {
1214
+	body, _, err := cli.call("GET", "/auth", nil)
1215
+	if err != nil {
1216
+		return "", err
1217
+	}
1218
+
1219
+	var out auth.AuthConfig
1220
+	err = json.Unmarshal(body, &out)
1221
+	if err != nil {
1222
+		return "", err
1223
+	}
1224
+
1225
+	// If condition AND the login failed
1226
+	if condition && out.Username == "" {
1227
+		if err := cli.CmdLogin(""); err != nil {
1228
+			return "", err
1229
+		}
1230
+
1231
+		body, _, err = cli.call("GET", "/auth", nil)
1232
+		if err != nil {
1233
+			return "", err
1234
+		}
1235
+		err = json.Unmarshal(body, &out)
1236
+		if err != nil {
1237
+			return "", err
1238
+		}
1239
+
1240
+		if out.Username == "" {
1241
+			return "", fmt.Errorf("Please login prior to %s. ('docker login')", action)
1242
+		}
1243
+	}
1244
+	return out.Username, nil
1245
+}
1246
+
1213 1247
 func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, error) {
1214 1248
 	var params io.Reader
1215 1249
 	if data != nil {
... ...
@@ -1220,7 +1254,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
1220 1220
 		params = bytes.NewBuffer(buf)
1221 1221
 	}
1222 1222
 
1223
-	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d", cli.host, cli.port)+path, params)
1223
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), params)
1224 1224
 	if err != nil {
1225 1225
 		return nil, -1, err
1226 1226
 	}
... ...
@@ -1252,7 +1286,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1252 1252
 	if (method == "POST" || method == "PUT") && in == nil {
1253 1253
 		in = bytes.NewReader([]byte{})
1254 1254
 	}
1255
-	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, path), in)
1255
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), in)
1256 1256
 	if err != nil {
1257 1257
 		return err
1258 1258
 	}
... ...
@@ -1276,14 +1310,35 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1276 1276
 		return fmt.Errorf("error: %s", body)
1277 1277
 	}
1278 1278
 
1279
-	if _, err := io.Copy(out, resp.Body); err != nil {
1280
-		return err
1279
+	if resp.Header.Get("Content-Type") == "application/json" {
1280
+		type Message struct {
1281
+			Status   string `json:"status,omitempty"`
1282
+			Progress string `json:"progress,omitempty"`
1283
+		}
1284
+		dec := json.NewDecoder(resp.Body)
1285
+		for {
1286
+			var m Message
1287
+			if err := dec.Decode(&m); err == io.EOF {
1288
+				break
1289
+			} else if err != nil {
1290
+				return err
1291
+			}
1292
+			if m.Progress != "" {
1293
+				fmt.Fprintf(out, "Downloading %s\r", m.Progress)
1294
+			} else {
1295
+				fmt.Fprintf(out, "%s\n", m.Status)
1296
+			}
1297
+		}
1298
+	} else {
1299
+		if _, err := io.Copy(out, resp.Body); err != nil {
1300
+			return err
1301
+		}
1281 1302
 	}
1282 1303
 	return nil
1283 1304
 }
1284 1305
 
1285 1306
 func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error {
1286
-	req, err := http.NewRequest(method, path, nil)
1307
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", API_VERSION, path), nil)
1287 1308
 	if err != nil {
1288 1309
 		return err
1289 1310
 	}
... ...
@@ -1333,6 +1388,33 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error {
1333 1333
 
1334 1334
 }
1335 1335
 
1336
+func (cli *DockerCli) resizeTty(id string) {
1337
+	ws, err := term.GetWinsize(os.Stdin.Fd())
1338
+	if err != nil {
1339
+		utils.Debugf("Error getting size: %s", err)
1340
+	}
1341
+	v := url.Values{}
1342
+	v.Set("h", strconv.Itoa(int(ws.Height)))
1343
+	v.Set("w", strconv.Itoa(int(ws.Width)))
1344
+	if _, _, err := cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil); err != nil {
1345
+		utils.Debugf("Error resize: %s", err)
1346
+	}
1347
+}
1348
+
1349
+func (cli *DockerCli) monitorTtySize(id string) {
1350
+	cli.resizeTty(id)
1351
+
1352
+	c := make(chan os.Signal, 1)
1353
+	signal.Notify(c, syscall.SIGWINCH)
1354
+	go func() {
1355
+		for sig := range c {
1356
+			if sig == syscall.SIGWINCH {
1357
+				cli.resizeTty(id)
1358
+			}
1359
+		}
1360
+	}()
1361
+}
1362
+
1336 1363
 func Subcmd(name, signature, description string) *flag.FlagSet {
1337 1364
 	flags := flag.NewFlagSet(name, flag.ContinueOnError)
1338 1365
 	flags.Usage = func() {
... ...
@@ -1342,8 +1424,8 @@ func Subcmd(name, signature, description string) *flag.FlagSet {
1342 1342
 	return flags
1343 1343
 }
1344 1344
 
1345
-func NewDockerCli(host string, port int) *DockerCli {
1346
-	return &DockerCli{host, port}
1345
+func NewDockerCli(addr string, port int) *DockerCli {
1346
+	return &DockerCli{addr, port}
1347 1347
 }
1348 1348
 
1349 1349
 type DockerCli struct {
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"encoding/json"
5 5
 	"flag"
6 6
 	"fmt"
7
+	"github.com/dotcloud/docker/term"
7 8
 	"github.com/dotcloud/docker/utils"
8 9
 	"github.com/kr/pty"
9 10
 	"io"
... ...
@@ -754,6 +755,14 @@ func (container *Container) Wait() int {
754 754
 	return container.State.ExitCode
755 755
 }
756 756
 
757
+func (container *Container) Resize(h, w int) error {
758
+	pty, ok := container.ptyMaster.(*os.File)
759
+	if !ok {
760
+		return fmt.Errorf("ptyMaster does not have Fd() method")
761
+	}
762
+	return term.SetWinsize(pty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
763
+}
764
+
757 765
 func (container *Container) ExportRw() (Archive, error) {
758 766
 	return Tar(container.rwPath(), Uncompressed)
759 767
 }
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"os"
11 11
 	"os/signal"
12 12
 	"strconv"
13
+	"strings"
13 14
 	"syscall"
14 15
 )
15 16
 
... ...
@@ -23,18 +24,38 @@ func main() {
23 23
 		docker.SysInit()
24 24
 		return
25 25
 	}
26
+	host := "127.0.0.1"
27
+	port := 4243
26 28
 	// FIXME: Switch d and D ? (to be more sshd like)
27 29
 	flDaemon := flag.Bool("d", false, "Daemon mode")
28 30
 	flDebug := flag.Bool("D", false, "Debug mode")
29 31
 	flAutoRestart := flag.Bool("r", false, "Restart previously running containers")
30 32
 	bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
31 33
 	pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
34
+	flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to")
32 35
 	flag.Parse()
33 36
 	if *bridgeName != "" {
34 37
 		docker.NetworkBridgeIface = *bridgeName
35 38
 	} else {
36 39
 		docker.NetworkBridgeIface = docker.DefaultNetworkBridge
37 40
 	}
41
+
42
+	if strings.Contains(*flHost, ":") {
43
+		hostParts := strings.Split(*flHost, ":")
44
+		if len(hostParts) != 2 {
45
+			log.Fatal("Invalid bind address format.")
46
+			os.Exit(-1)
47
+		}
48
+		if hostParts[0] != "" {
49
+			host = hostParts[0]
50
+		}
51
+		if p, err := strconv.Atoi(hostParts[1]); err == nil {
52
+			port = p
53
+		}
54
+	} else {
55
+		host = *flHost
56
+	}
57
+
38 58
 	if *flDebug {
39 59
 		os.Setenv("DEBUG", "1")
40 60
 	}
... ...
@@ -44,12 +65,12 @@ func main() {
44 44
 			flag.Usage()
45 45
 			return
46 46
 		}
47
-		if err := daemon(*pidfile, *flAutoRestart); err != nil {
47
+		if err := daemon(*pidfile, host, port, *flAutoRestart); err != nil {
48 48
 			log.Fatal(err)
49 49
 			os.Exit(-1)
50 50
 		}
51 51
 	} else {
52
-		if err := docker.ParseCommands(flag.Args()...); err != nil {
52
+		if err := docker.ParseCommands(host, port, flag.Args()...); err != nil {
53 53
 			log.Fatal(err)
54 54
 			os.Exit(-1)
55 55
 		}
... ...
@@ -83,7 +104,10 @@ func removePidFile(pidfile string) {
83 83
 	}
84 84
 }
85 85
 
86
-func daemon(pidfile string, autoRestart bool) error {
86
+func daemon(pidfile, addr string, port int, autoRestart bool) error {
87
+	if addr != "127.0.0.1" {
88
+		log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
89
+	}
87 90
 	if err := createPidFile(pidfile); err != nil {
88 91
 		log.Fatal(err)
89 92
 	}
... ...
@@ -103,5 +127,5 @@ func daemon(pidfile string, autoRestart bool) error {
103 103
 		return err
104 104
 	}
105 105
 
106
-	return docker.ListenAndServe("0.0.0.0:4243", server, true)
106
+	return docker.ListenAndServe(fmt.Sprintf("%s:%d", addr, port), server, true)
107 107
 }
... ...
@@ -1,3 +1,7 @@
1
+:title: Remote API
2
+:description: API Documentation for Docker
3
+:keywords: API, Docker, rcli, REST, documentation
4
+
1 5
 =================
2 6
 Docker Remote API
3 7
 =================
... ...
@@ -118,7 +122,8 @@ Create a container
118 118
 	.. sourcecode:: http
119 119
 
120 120
 	   HTTP/1.1 201 OK
121
-	   
121
+	   Content-Type: application/json
122
+
122 123
 	   {
123 124
 		"Id":"e90e34656806"
124 125
 		"Warnings":[]
... ...
@@ -373,7 +378,7 @@ Attach to a container
373 373
 
374 374
 .. http:post:: /containers/(id)/attach
375 375
 
376
-	Stop the container ``id``
376
+	Attach to the container ``id``
377 377
 
378 378
 	**Example request**:
379 379
 
... ...
@@ -1,6 +1,6 @@
1
-:title: docker documentation
1
+:title: API Documentation
2 2
 :description: docker documentation
3
-:keywords:
3
+:keywords: docker, ipa, documentation
4 4
 
5 5
 API's
6 6
 =============
... ...
@@ -1,4 +1,4 @@
1
-:title: docker Registry documentation
1
+:title: Registry Documentation
2 2
 :description: Documentation for docker Registry and Registry API
3 3
 :keywords: docker, registry, api, index
4 4
 
... ...
@@ -301,7 +301,7 @@ POST /v1/users
301 301
     {"email": "sam@dotcloud.com", "password": "toto42", "username": "foobar"'}
302 302
 
303 303
 **Validation**:
304
-    - **username** : min 4 character, max 30 characters, all lowercase no special characters.
304
+    - **username** : min 4 character, max 30 characters, must match the regular expression [a-z0-9_].
305 305
     - **password**: min 5 characters
306 306
 
307 307
 **Valid**: return HTTP 200
... ...
@@ -345,6 +345,11 @@ GET /v1/users
345 345
 
346 346
 The Registry does not know anything about users. Even though repositories are under usernames, it’s just a namespace for the registry. Allowing us to implement organizations or different namespaces per user later, without modifying the Registry’s API.
347 347
 
348
+The following naming restrictions apply:
349
+
350
+- Namespaces must match the same regular expression as usernames (See 4.2.1.)
351
+- Repository names must match the regular expression [a-zA-Z0-9-_.]
352
+
348 353
 4.3.1 Get all tags
349 354
 ^^^^^^^^^^^^^^^^^^
350 355
 
... ...
@@ -14,7 +14,8 @@ To list available commands, either run ``docker`` with no parameters or execute
14 14
 ``docker help``::
15 15
 
16 16
   $ docker
17
-    Usage: docker COMMAND [arg...]
17
+    Usage: docker [OPTIONS] COMMAND [arg...]
18
+      -H="127.0.0.1:4243": Host:port to bind/connect to
18 19
 
19 20
     A self-sufficient runtime for linux containers.
20 21
 
... ...
@@ -1,3 +1,7 @@
1
+:title: Attach Command
2
+:description: Attach to a running container
3
+:keywords: attach, container, docker, documentation
4
+
1 5
 ===========================================
2 6
 ``attach`` -- Attach to a running container
3 7
 ===========================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Build Command
2
+:description: Build a new image from the Dockerfile passed via stdin
3
+:keywords: build, docker, container, documentation
4
+
1 5
 ========================================================
2 6
 ``build`` -- Build a container from Dockerfile via stdin
3 7
 ========================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Commit Command
2
+:description: Create a new image from a container's changes
3
+:keywords: commit, docker, container, documentation
4
+
1 5
 ===========================================================
2 6
 ``commit`` -- Create a new image from a container's changes
3 7
 ===========================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Diff Command
2
+:description: Inspect changes on a container's filesystem
3
+:keywords: diff, docker, container, documentation
4
+
1 5
 =======================================================
2 6
 ``diff`` -- Inspect changes on a container's filesystem
3 7
 =======================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Export Command
2
+:description: Export the contents of a filesystem as a tar archive
3
+:keywords: export, docker, container, documentation
4
+
1 5
 =================================================================
2 6
 ``export`` -- Stream the contents of a container as a tar archive
3 7
 =================================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: History Command
2
+:description: Show the history of an image
3
+:keywords: history, docker, container, documentation
4
+
1 5
 ===========================================
2 6
 ``history`` -- Show the history of an image
3 7
 ===========================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Images Command
2
+:description: List images
3
+:keywords: images, docker, container, documentation
4
+
1 5
 =========================
2 6
 ``images`` -- List images
3 7
 =========================
... ...
@@ -1,3 +1,7 @@
1
+:title: Import Command
2
+:description: Create a new filesystem image from the contents of a tarball
3
+:keywords: import, tarball, docker, url, documentation
4
+
1 5
 ==========================================================================
2 6
 ``import`` -- Create a new filesystem image from the contents of a tarball
3 7
 ==========================================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Info Command
2
+:description: Display system-wide information.
3
+:keywords: info, docker, information, documentation
4
+
1 5
 ===========================================
2 6
 ``info`` -- Display system-wide information
3 7
 ===========================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Inspect Command
2
+:description: Return low-level information on a container
3
+:keywords: inspect, container, docker, documentation
4
+
1 5
 ==========================================================
2 6
 ``inspect`` -- Return low-level information on a container
3 7
 ==========================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Kill Command
2
+:description: Kill a running container
3
+:keywords: kill, container, docker, documentation
4
+
1 5
 ====================================
2 6
 ``kill`` -- Kill a running container
3 7
 ====================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Login Command
2
+:description: Register or Login to the docker registry server
3
+:keywords: login, docker, documentation
4
+
1 5
 ============================================================
2 6
 ``login`` -- Register or Login to the docker registry server
3 7
 ============================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Logs Command
2
+:description: Fetch the logs of a container
3
+:keywords: logs, container, docker, documentation
4
+
1 5
 =========================================
2 6
 ``logs`` -- Fetch the logs of a container
3 7
 =========================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Port Command
2
+:description: Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
3
+:keywords: port, docker, container, documentation
4
+
1 5
 =========================================================================
2 6
 ``port`` -- Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
3 7
 =========================================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Ps Command
2
+:description: List containers
3
+:keywords: ps, docker, documentation, container
4
+
1 5
 =========================
2 6
 ``ps`` -- List containers
3 7
 =========================
... ...
@@ -1,3 +1,7 @@
1
+:title: Pull Command
2
+:description: Pull an image or a repository from the registry
3
+:keywords: pull, image, repo, repository, documentation, docker
4
+
1 5
 =========================================================================
2 6
 ``pull`` -- Pull an image or a repository from the docker registry server
3 7
 =========================================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Push Command
2
+:description: Push an image or a repository to the registry
3
+:keywords: push, docker, image, repository, documentation, repo
4
+
1 5
 =======================================================================
2 6
 ``push`` -- Push an image or a repository to the docker registry server
3 7
 =======================================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Restart Command
2
+:description: Restart a running container
3
+:keywords: restart, container, docker, documentation
4
+
1 5
 ==========================================
2 6
 ``restart`` -- Restart a running container
3 7
 ==========================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Rm Command
2
+:description: Remove a container
3
+:keywords: remove, container, docker, documentation, rm
4
+
1 5
 ============================
2 6
 ``rm`` -- Remove a container
3 7
 ============================
... ...
@@ -1,3 +1,7 @@
1
+:title: Rmi Command
2
+:description: Remove an image
3
+:keywords: rmi, remove, image, docker, documentation
4
+
1 5
 ==========================
2 6
 ``rmi`` -- Remove an image
3 7
 ==========================
... ...
@@ -1,3 +1,7 @@
1
+:title: Run Command
2
+:description: Run a command in a new container
3
+:keywords: run, container, docker, documentation 
4
+
1 5
 ===========================================
2 6
 ``run`` -- Run a command in a new container
3 7
 ===========================================
... ...
@@ -19,5 +23,5 @@
19 19
       -t=false: Allocate a pseudo-tty
20 20
       -u="": Username or UID
21 21
       -d=[]: Set custom dns servers for the container
22
-      -v=[]: Creates a new volumes and mount it at the specified path.
22
+      -v=[]: Creates a new volume and mounts it at the specified path.
23 23
       -volumes-from="": Mount all volumes from the given container.
... ...
@@ -1,3 +1,7 @@
1
+:title: Search Command
2
+:description: Searches for the TERM parameter on the Docker index and prints out a list of repositories that match.
3
+:keywords: search, docker, image, documentation 
4
+
1 5
 ===================================================================
2 6
 ``search`` -- Search for an image in the docker index
3 7
 ===================================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Start Command
2
+:description: Start a stopped container
3
+:keywords: start, docker, container, documentation
4
+
1 5
 ======================================
2 6
 ``start`` -- Start a stopped container
3 7
 ======================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Stop Command
2
+:description: Stop a running container
3
+:keywords: stop, container, docker, documentation
4
+
1 5
 ====================================
2 6
 ``stop`` -- Stop a running container
3 7
 ====================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Tag Command
2
+:description: Tag an image into a repository
3
+:keywords: tag, docker, image, repository, documentation, repo
4
+
1 5
 =========================================
2 6
 ``tag`` -- Tag an image into a repository
3 7
 =========================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Version Command
2
+:description: 
3
+:keywords: version, docker, documentation
4
+
1 5
 ==================================================
2 6
 ``version`` -- Show the docker version information
3 7
 ==================================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Wait Command
2
+:description: Block until a container stops, then print its exit code.
3
+:keywords: wait, docker, container, documentation
4
+
1 5
 ===================================================================
2 6
 ``wait`` -- Block until a container stops, then print its exit code
3 7
 ===================================================================
... ...
@@ -1,6 +1,6 @@
1
-:title: docker documentation
1
+:title: Commands
2 2
 :description: -- todo: change me
3
-:keywords: todo: change me
3
+:keywords: todo, commands, command line, help, docker, documentation
4 4
 
5 5
 
6 6
 Commands
... ...
@@ -1,4 +1,4 @@
1
-:title: Building blocks
1
+:title: Building Blocks
2 2
 :description: An introduction to docker and standard containers?
3 3
 :keywords: containers, lxc, concepts, explanation
4 4
 
... ...
@@ -1,6 +1,6 @@
1 1
 :title: Introduction
2 2
 :description: An introduction to docker and standard containers?
3
-:keywords: containers, lxc, concepts, explanation
3
+:keywords: containers, lxc, concepts, explanation, docker, documentation
4 4
 
5 5
 
6 6
 :note: This version of the introduction is temporary, just to make sure we don't break the links from the website when the documentation is updated
... ...
@@ -1,6 +1,6 @@
1
-:title: docker documentation
1
+:title: Concepts
2 2
 :description: -- todo: change me
3
-:keywords: todo: change me
3
+:keywords: concepts, documentation, docker, containers
4 4
 
5 5
 
6 6
 
... ...
@@ -1,3 +1,7 @@
1
+:title: Contribution Guidelines
2
+:description: Contribution guidelines: create issues, convetions, pull requests
3
+:keywords: contributing, docker, documentation, help, guideline
4
+
1 5
 Contributing to Docker
2 6
 ======================
3 7
 
... ...
@@ -40,7 +40,7 @@ We attach to the new container to see what is going on. Ctrl-C to disconnect
40 40
 
41 41
 .. code-block:: bash
42 42
 
43
-    BUILD_IMG=$(docker commit $BUILD_JOB _/builds/github.com/hykes/helloflask/master)
43
+    BUILD_IMG=$(docker commit $BUILD_JOB _/builds/github.com/shykes/helloflask/master)
44 44
 
45 45
 Save the changed we just made in the container to a new image called "_/builds/github.com/hykes/helloflask/master" and save the image id in the BUILD_IMG variable name.
46 46
 
... ...
@@ -58,7 +58,7 @@ Use the new image we just created and create a new container with network port 5
58 58
 .. code-block:: bash
59 59
 
60 60
     docker logs $WEB_WORKER
61
-     * Running on \http://0.0.0.0:5000/
61
+     * Running on http://0.0.0.0:5000/
62 62
 
63 63
 view the logs for the new container using the WEB_WORKER variable, and if everything worked as planned you should see the line "Running on http://0.0.0.0:5000/" in the log output.
64 64
 
... ...
@@ -70,7 +70,8 @@ lookup the public-facing port which is NAT-ed store the private port used by the
70 70
 
71 71
 .. code-block:: bash
72 72
 
73
-    curl \http://`hostname`:$WEB_PORT
73
+    # install curl if necessary, then ...
74
+    curl http://127.0.0.1:$WEB_PORT
74 75
       Hello world!
75 76
 
76 77
 access the web app using curl. If everything worked as planned you should see the line "Hello world!" inside of your console.
... ...
@@ -1,3 +1,7 @@
1
+:title: FAQ
2
+:description: Most frequently asked questions.
3
+:keywords: faq, questions, documentation, docker
4
+
1 5
 FAQ
2 6
 ===
3 7
 
... ...
@@ -1,3 +1,7 @@
1
+:title: Index Environment Variable
2
+:description: Setting this environment variable on the docker server will change the URL docker index.
3
+:keywords: docker, index environment variable, documentation 
4
+
1 5
 =================================
2 6
 Docker Index Environment Variable
3 7
 =================================
... ...
@@ -1,3 +1,7 @@
1
+:title: Installation on Amazon EC2 
2
+:description: Docker installation on Amazon EC2 with a single vagrant command. Vagrant 1.1 or higher is required.
3
+:keywords: amazon ec2, virtualization, cloud, docker, documentation, installation
4
+
1 5
 Amazon EC2
2 6
 ==========
3 7
 
... ...
@@ -1,3 +1,7 @@
1
+:title: Installation on Arch Linux
2
+:description: Docker installation on Arch Linux. 
3
+:keywords: arch linux, virtualization, docker, documentation, installation
4
+
1 5
 .. _arch_linux:
2 6
 
3 7
 Arch Linux
... ...
@@ -1,3 +1,7 @@
1
+:title: Installation from Binaries
2
+:description: This instruction set is meant for hackers who want to try out Docker on a variety of environments.
3
+:keywords: binaries, installation, docker, documentation, linux
4
+
1 5
 .. _binaries:
2 6
 
3 7
 Binaries
... ...
@@ -23,7 +27,7 @@ But we know people have had success running it under
23 23
 Dependencies:
24 24
 -------------
25 25
 
26
-* 3.8 Kernel
26
+* 3.8 Kernel (read more about :ref:`kernel`)
27 27
 * AUFS filesystem support
28 28
 * lxc
29 29
 * bsdtar
... ...
@@ -1,6 +1,6 @@
1
-:title: docker documentation
1
+:title: Documentation
2 2
 :description: -- todo: change me
3
-:keywords: todo: change me
3
+:keywords: todo, docker, documentation, installation, OS support
4 4
 
5 5
 
6 6
 
... ...
@@ -1,63 +1,35 @@
1
+:title: Kernel Requirements
2
+:description: Kernel supports
3
+:keywords: kernel requirements, kernel support, docker, installation, cgroups, namespaces
4
+
1 5
 .. _kernel:
2 6
 
3 7
 Kernel Requirements
4 8
 ===================
5 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
+
6 19
   The officially supported kernel is the one recommended by the
7 20
   :ref:`ubuntu_linux` installation path. It is the one that most developers
8 21
   will use, and the one that receives the most attention from the core
9 22
   contributors. If you decide to go with a different kernel and hit a bug,
10 23
   please try to reproduce it with the official kernels first.
11 24
 
12
-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,
13 26
 here is some technical background about the features (both optional and
14 27
 mandatory) that docker needs to run successfully.
15 28
 
16
-In short, you need kernel version 3.8 (or above), compiled to include
17
-`AUFS support <http://aufs.sourceforge.net/>`_. Of course, you need to
18
-enable cgroups and namespaces.
19
-
20
-
21
-Namespaces and Cgroups
22
-
23
-You need to enable namespaces and cgroups, to the extend of what is needed
24
-to run LXC containers. Technically, while namespaces have been introduced
25
-in the early 2.6 kernels, we do not advise to try any kernel before 2.6.32
26
-to run LXC containers. Note that 2.6.32 has some documented issues regarding
27
-network namespace setup and teardown; those issues are not a risk if you
28
-run containers in a private environment, but can lead to denial-of-service
29
-attacks if you want to run untrusted code in your containers. For more details,
30
-see `[LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
31
-
32
-Kernels 2.6.38, and every version since 3.2, have been deployed successfully
33
-to run containerized production workloads. Feature-wise, there is no huge
34
-improvement between 2.6.38 and up to 3.6 (as far as docker is concerned!).
35
-
36
-Starting with version 3.7, the kernel has basic support for
37
-`Checkpoint/Restore In Userspace <http://criu.org/>`_, which is not used by
38
-docker at this point, but allows to suspend the state of a container to
39
-disk and resume it later.
40
-
41
-Version 3.8 provides improvements in stability, which are deemed necessary
42
-for the operation of docker. Versions 3.2 to 3.5 have been shown to
43
-exhibit a reproducible bug (for more details, see issue
44
-`#407 <https://github.com/dotcloud/docker/issues/407>`_).
45
-
46
-Version 3.8 also brings better support for the
47
-`setns() syscall <http://lwn.net/Articles/531381/>`_ -- but this should not
48
-be a concern since docker does not leverage on this feature for now.
49
-
50
-If you want a technical overview about those concepts, you might
51
-want to check those articles on dotCloud's blog:
52
-`about namespaces <http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part>`_
53
-and `about cgroups <http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-24-c>`_.
54
-
29
+Linux version 3.8 or above
30
+--------------------------
55 31
 
56
-Important Note About Pre-3.8 Kernels
57
-
58
-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.
59 33
 In some circumstances, you will experience kernel "oopses", or even crashes.
60 34
 The symptoms include:
61 35
 
... ...
@@ -77,6 +49,36 @@ detects something older than 3.8.
77 77
 See issue `#407 <https://github.com/dotcloud/docker/issues/407>`_ for details.
78 78
 
79 79
 
80
+AUFS support
81
+------------
82
+
83
+Docker currently relies on AUFS, an unioning filesystem.
84
+While AUFS is included in the kernels built by the Debian and Ubuntu
85
+distributions, is not part of the standard kernel. This means that if
86
+you decide to roll your own kernel, you will have to patch your
87
+kernel tree to add AUFS. The process is documented on
88
+`AUFS webpage <http://aufs.sourceforge.net/>`_.
89
+
90
+
91
+Cgroups and namespaces
92
+----------------------
93
+
94
+You need to enable namespaces and cgroups, to the extend of what is needed
95
+to run LXC containers. Technically, while namespaces have been introduced
96
+in the early 2.6 kernels, we do not advise to try any kernel before 2.6.32
97
+to run LXC containers. Note that 2.6.32 has some documented issues regarding
98
+network namespace setup and teardown; those issues are not a risk if you
99
+run containers in a private environment, but can lead to denial-of-service
100
+attacks if you want to run untrusted code in your containers. For more details,
101
+see `[LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
102
+
103
+Kernels 2.6.38, and every version since 3.2, have been deployed successfully
104
+to run containerized production workloads. Feature-wise, there is no huge
105
+improvement between 2.6.38 and up to 3.6 (as far as docker is concerned!).
106
+
107
+
108
+
109
+
80 110
 Extra Cgroup Controllers
81 111
 ------------------------
82 112
 
... ...
@@ -111,39 +113,3 @@ And replace it by the following one::
111 111
     GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount"
112 112
 
113 113
 Then run ``update-grub``, and reboot.
114
-
115
-
116
-AUFS
117
-
118
-Docker currently relies on AUFS, an unioning filesystem.
119
-While AUFS is included in the kernels built by the Debian and Ubuntu
120
-distributions, is not part of the standard kernel. This means that if
121
-you decide to roll your own kernel, you will have to patch your
122
-kernel tree to add AUFS. The process is documented on
123
-`AUFS webpage <http://aufs.sourceforge.net/>`_.
124
-
125
-Note: the AUFS patch is fairly intrusive, but for the record, people have
126
-successfully applied GRSEC and AUFS together, to obtain hardened production
127
-kernels.
128
-
129
-If you want more information about that topic, there is an
130
-`article about AUFS on dotCloud's blog 
131
-<http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-34-a>`_.
132
-
133
-
134
-BTRFS, ZFS, OverlayFS...
135
-
136
-There is ongoing development on docker, to implement support for
137
-`BTRFS <http://en.wikipedia.org/wiki/Btrfs>`_
138
-(see github issue `#443 <https://github.com/dotcloud/docker/issues/443>`_).
139
-
140
-People have also showed interest for `ZFS <http://en.wikipedia.org/wiki/ZFS>`_
141
-(using e.g. `ZFS-on-Linux <http://zfsonlinux.org/>`_) and OverlayFS.
142
-The latter is functionally close to AUFS, and it might end up being included
143
-in the stock kernel; so it's a strong candidate!
144
-
145
-Would you like to `contribute
146
-<https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`_
147
-support for your favorite filesystem?
... ...
@@ -1,3 +1,7 @@
1
+:title: Rackspace Cloud Installation
2
+:description: Installing Docker on Ubuntu proviced by Rackspace
3
+:keywords: Rackspace Cloud, installation, docker, linux, ubuntu
4
+
1 5
 ===============
2 6
 Rackspace Cloud
3 7
 ===============
... ...
@@ -1,3 +1,7 @@
1
+:title: Requirements and Installation on Ubuntu Linux
2
+:description: Please note this project is currently under heavy development. It should not be used in production.
3
+:keywords: Docker, Docker documentation, requirements, virtualbox, vagrant, git, ssh, putty, cygwin, linux
4
+
1 5
 .. _ubuntu_linux:
2 6
 
3 7
 Ubuntu Linux
... ...
@@ -12,7 +16,7 @@ Right now, the officially supported distribution are:
12 12
 
13 13
 Docker has the following dependencies
14 14
 
15
-* Linux kernel 3.8
15
+* Linux kernel 3.8 (read more about :ref:`kernel`)
16 16
 * AUFS file system support (we are working on BTRFS support as an alternative)
17 17
 
18 18
 .. _ubuntu_precise:
... ...
@@ -34,7 +38,7 @@ Due to a bug in LXC docker works best on the 3.8 kernel. Precise comes with a 3.
34 34
 .. code-block:: bash
35 35
 
36 36
    # install the backported kernel
37
-   sudo apt-get update && sudo apt-get install linux-image-3.8.0-19-generic
37
+   sudo apt-get update && sudo apt-get install linux-image-generic-lts-raring
38 38
 
39 39
    # reboot
40 40
    sudo reboot
... ...
@@ -1,3 +1,7 @@
1
+:title: Upgrading
2
+:description: These instructions are for upgrading Docker
3
+:keywords: Docker, Docker documentation, upgrading docker, upgrade
4
+
1 5
 .. _upgrading:
2 6
 
3 7
 Upgrading
... ...
@@ -1,3 +1,6 @@
1
+:title: Using Vagrant (Mac, Linux)
2
+:description: This guide will setup a new virtualbox virtual machine with docker installed on your computer.
3
+:keywords: Docker, Docker documentation, virtualbox, vagrant, git, ssh, putty, cygwin
1 4
 
2 5
 .. _install_using_vagrant:
3 6
 
... ...
@@ -1,6 +1,6 @@
1
-:title: docker documentation
2
-:description: docker documentation
3
-:keywords:
1
+:title: Documentation
2
+:description: -- todo: change me
3
+:keywords: todo, docker, documentation, installation, usage, examples, contributing, faq, command line, concepts
4 4
 
5 5
 Documentation
6 6
 =============
... ...
@@ -1,6 +1,6 @@
1
-:title: Base commands
1
+:title: Basic Commands
2 2
 :description: Common usage and commands
3
-:keywords: Examples, Usage
3
+:keywords: Examples, Usage, basic commands, docker, documentation, examples
4 4
 
5 5
 
6 6
 The basics
... ...
@@ -33,6 +33,19 @@ Running an interactive shell
33 33
   # allocate a tty, attach stdin and stdout
34 34
   docker run -i -t base /bin/bash
35 35
 
36
+Bind Docker to another host/port
37
+--------------------------------
38
+
39
+If you want Docker to listen to another port and bind to another ip
40
+use -host and -port on both deamon and client
41
+
42
+.. code-block:: bash
43
+
44
+   # Run docker in daemon mode
45
+   sudo <path to>/docker -H 0.0.0.0:5555 &
46
+   # Download a base image
47
+   docker -H :5555 pull base
48
+
36 49
 
37 50
 Starting a long-running worker process
38 51
 --------------------------------------
... ...
@@ -1,3 +1,7 @@
1
+:title: Docker Builder
2
+:description: Docker Builder specifes a simple DSL which allows you to automate the steps you would normally manually take to create an image.
3
+:keywords: builder, docker, Docker Builder, automation, image creation
4
+
1 5
 ==============
2 6
 Docker Builder
3 7
 ==============
... ...
@@ -1,6 +1,6 @@
1
-:title: docker documentation
1
+:title: Documentation
2 2
 :description: -- todo: change me
3
-:keywords: todo: change me
3
+:keywords: todo, docker, documentation, basic, builder
4 4
 
5 5
 
6 6
 
... ...
@@ -1,3 +1,6 @@
1
+:title: Puppet Usage
2
+:description: Installating and using Puppet
3
+:keywords: puppet, installation, usage, docker, documentation
1 4
 
2 5
 .. _install_using_puppet:
3 6
 
... ...
@@ -22,9 +25,9 @@ Installation
22 22
 The module is available on the `Puppet Forge <https://forge.puppetlabs.com/garethr/docker/>`_
23 23
 and can be installed using the built-in module tool.
24 24
 
25
-   .. code-block:: bash
25
+.. code-block:: bash
26 26
 
27
-      puppet module install garethr/docker
27
+   puppet module install garethr/docker
28 28
 
29 29
 It can also be found on `GitHub <https://www.github.com/garethr/garethr-docker>`_ 
30 30
 if you would rather download the source.
... ...
@@ -38,9 +41,9 @@ for managing images and containers.
38 38
 Installation
39 39
 ~~~~~~~~~~~~
40 40
 
41
-   .. code-block:: ruby
41
+.. code-block:: ruby
42 42
 
43
-      include 'docker'
43
+  include 'docker'
44 44
 
45 45
 Images
46 46
 ~~~~~~
... ...
@@ -48,26 +51,26 @@ Images
48 48
 The next step is probably to install a docker image, for this we have a
49 49
 defined type which can be used like so:
50 50
 
51
-   .. code-block:: ruby
51
+.. code-block:: ruby
52 52
 
53
-      docker::image { 'base': }
53
+  docker::image { 'base': }
54 54
 
55 55
 This is equivalent to running:
56 56
 
57
-   .. code-block:: bash
57
+.. code-block:: bash
58 58
 
59
-      docker pull base
59
+  docker pull base
60 60
 
61 61
 Note that it will only if the image of that name does not already exist.
62 62
 This is downloading a large binary so on first run can take a while.
63 63
 For that reason this define turns off the default 5 minute timeout
64 64
 for exec. Note that you can also remove images you no longer need with:
65 65
 
66
-   .. code-block:: ruby
66
+.. code-block:: ruby
67 67
 
68
-      docker::image { 'base':
69
-        ensure => 'absent',
70
-      }
68
+  docker::image { 'base':
69
+    ensure => 'absent',
70
+  }
71 71
 
72 72
 Containers
73 73
 ~~~~~~~~~~
... ...
@@ -75,35 +78,35 @@ Containers
75 75
 Now you have an image you can run commands within a container managed by
76 76
 docker.
77 77
 
78
-   .. code-block:: ruby
78
+.. code-block:: ruby
79 79
 
80
-      docker::run { 'helloworld':
81
-        image   => 'base',
82
-        command => '/bin/sh -c "while true; do echo hello world; sleep 1; done"',
83
-      }
80
+  docker::run { 'helloworld':
81
+    image   => 'base',
82
+    command => '/bin/sh -c "while true; do echo hello world; sleep 1; done"',
83
+  }
84 84
 
85 85
 This is equivalent to running the following command, but under upstart:
86 86
 
87
-   .. code-block:: bash
87
+.. code-block:: bash
88 88
 
89
-      docker run -d base /bin/sh -c "while true; do echo hello world; sleep 1; done"
89
+  docker run -d base /bin/sh -c "while true; do echo hello world; sleep 1; done"
90 90
 
91 91
 Run also contains a number of optional parameters:
92 92
 
93
-   .. code-block:: ruby
94
-
95
-      docker::run { 'helloworld':
96
-        image        => 'base',
97
-        command      => '/bin/sh -c "while true; do echo hello world; sleep 1; done"',
98
-        ports        => ['4444', '4555'],
99
-        volumes      => ['/var/lib/counchdb', '/var/log'],
100
-        volumes_from => '6446ea52fbc9',
101
-        memory_limit => 10485760, # bytes 
102
-        username     => 'example',
103
-        hostname     => 'example.com',
104
-        env          => ['FOO=BAR', 'FOO2=BAR2'],
105
-        dns          => ['8.8.8.8', '8.8.4.4'],
106
-      }
93
+.. code-block:: ruby
94
+
95
+  docker::run { 'helloworld':
96
+    image        => 'base',
97
+    command      => '/bin/sh -c "while true; do echo hello world; sleep 1; done"',
98
+    ports        => ['4444', '4555'],
99
+    volumes      => ['/var/lib/counchdb', '/var/log'],
100
+    volumes_from => '6446ea52fbc9',
101
+    memory_limit => 10485760, # bytes
102
+    username     => 'example',
103
+    hostname     => 'example.com',
104
+    env          => ['FOO=BAR', 'FOO2=BAR2'],
105
+    dns          => ['8.8.8.8', '8.8.4.4'],
106
+  }
107 107
 
108 108
 Note that ports, env, dns and volumes can be set with either a single string
109 109
 or as above with an array of values.
... ...
@@ -1,3 +1,7 @@
1
+:title: Working With Repositories
2
+:description: Generally, there are two types of repositories: Top-level repositories which are controlled by the people behind Docker, and user repositories.
3
+:keywords: repo, repositiores, usage, pull image, push image, image, documentation
4
+
1 5
 .. _working_with_the_repository:
2 6
 
3 7
 Working with the repository
... ...
@@ -8,7 +8,7 @@
8 8
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
9 9
     <meta name="google-site-verification" content="UxV66EKuPe87dgnH1sbrldrx6VsoWMrx5NjwkgUFxXI" />
10 10
 
11
-    <title>Docker - {{ meta['title'] if meta and meta['title'] else title }}</title>
11
+    <title>{{ meta['title'] if meta and meta['title'] else title }} - Docker Documentation</title>
12 12
 
13 13
     <meta name="description" content="{{ meta['description'] if meta }}" />
14 14
     <meta name="keywords" content="{{ meta['keywords'] if meta }}" />
... ...
@@ -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
 }
... ...
@@ -68,11 +68,13 @@ func init() {
68 68
 		registry: registry.NewRegistry(runtime.root),
69 69
 	}
70 70
 	// Retrieve the Image
71
-	if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout); err != nil {
71
+	if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, false); err != nil {
72 72
 		panic(err)
73 73
 	}
74 74
 }
75 75
 
76
+// FIXME: test that ImagePull(json=true) send correct json output
77
+
76 78
 func newTestRuntime() (*Runtime, error) {
77 79
 	root, err := ioutil.TempDir("", "docker-test")
78 80
 	if err != nil {
... ...
@@ -91,8 +91,8 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e
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 {
95
-		return "", err
94
+	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)\r", false), path); err != nil {
95
+		return err
96 96
 	}
97 97
 	// FIXME: Handle custom repo, tag comment, author
98 98
 	img, err = b.Commit(c, "", "", img.Comment, img.Author, nil)
... ...
@@ -291,8 +291,7 @@ 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 {
295
-	out = utils.NewWriteFlusher(out)
294
+func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string, json bool) error {
296 295
 	history, err := srv.registry.GetRemoteHistory(imgId, registry, token)
297 296
 	if err != nil {
298 297
 		return err
... ...
@@ -302,7 +301,7 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
302 302
 	// FIXME: Launch the getRemoteImage() in goroutines
303 303
 	for _, id := range history {
304 304
 		if !srv.runtime.graph.Exists(id) {
305
-			fmt.Fprintf(out, "Pulling %s metadata\r\n", id)
305
+			fmt.Fprintf(out, utils.FormatStatus("Pulling %s metadata", json), id)
306 306
 			imgJson, err := srv.registry.GetRemoteImageJson(id, registry, token)
307 307
 			if err != nil {
308 308
 				// FIXME: Keep goging in case of error?
... ...
@@ -314,12 +313,12 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
314 314
 			}
315 315
 
316 316
 			// Get the layer
317
-			fmt.Fprintf(out, "Pulling %s fs layer\r\n", img.Id)
317
+			fmt.Fprintf(out, utils.FormatStatus("Pulling %s fs layer", json), id)
318 318
 			layer, contentLength, err := srv.registry.GetRemoteImageLayer(img.Id, registry, token)
319 319
 			if err != nil {
320 320
 				return err
321 321
 			}
322
-			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, "Downloading %v/%v (%v)"), false, img); err != nil {
322
+			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, utils.FormatProgress("%v/%v (%v)", json), json), false, img); err != nil {
323 323
 				return err
324 324
 			}
325 325
 		}
... ...
@@ -327,9 +326,8 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
327 327
 	return nil
328 328
 }
329 329
 
330
-func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error {
331
-	out = utils.NewWriteFlusher(out)
332
-	fmt.Fprintf(out, "Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress())
330
+func (srv *Server) pullRepository(out io.Writer, remote, askedTag string, json bool) error {
331
+	fmt.Fprintf(out, utils.FormatStatus("Pulling repository %s from %s", json), remote, auth.IndexServerAddress())
333 332
 	repoData, err := srv.registry.GetRepositoryData(remote)
334 333
 	if err != nil {
335 334
 		return err
... ...
@@ -366,11 +364,11 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error
366 366
 			utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.Id)
367 367
 			continue
368 368
 		}
369
-		fmt.Fprintf(out, "Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote)
369
+		fmt.Fprintf(out, utils.FormatStatus("Pulling image %s (%s) from %s", json), img.Id, img.Tag, remote)
370 370
 		success := false
371 371
 		for _, ep := range repoData.Endpoints {
372
-			if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil {
373
-				fmt.Fprintf(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err)
372
+			if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens, json); err != nil {
373
+				fmt.Fprintf(out, utils.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint\n", json), askedTag, err)
374 374
 				continue
375 375
 			}
376 376
 			success = true
... ...
@@ -395,15 +393,16 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error
395 395
 	return nil
396 396
 }
397 397
 
398
-func (srv *Server) ImagePull(name, tag, registry string, out io.Writer) error {
398
+func (srv *Server) ImagePull(name, tag, registry string, out io.Writer, json bool) error {
399
+	out = utils.NewWriteFlusher(out)
399 400
 	if registry != "" {
400
-		if err := srv.pullImage(out, name, registry, nil); err != nil {
401
+		if err := srv.pullImage(out, name, registry, nil, json); err != nil {
401 402
 			return err
402 403
 		}
403 404
 		return nil
404 405
 	}
405 406
 
406
-	if err := srv.pullRepository(out, name, tag); err != nil {
407
+	if err := srv.pullRepository(out, name, tag, json); err != nil {
407 408
 		return err
408 409
 	}
409 410
 
... ...
@@ -570,7 +569,7 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st
570 570
 	}
571 571
 
572 572
 	// Send the layer
573
-	if err := srv.registry.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, ""), ep, token); err != nil {
573
+	if err := srv.registry.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, "", false), ep, token); err != nil {
574 574
 		return err
575 575
 	}
576 576
 	return nil
... ...
@@ -621,7 +620,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
621 621
 		if err != nil {
622 622
 			return err
623 623
 		}
624
-		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)")
624
+		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)\r", false)
625 625
 	}
626 626
 	img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
627 627
 	if err != nil {
... ...
@@ -776,6 +775,13 @@ func (srv *Server) ContainerWait(name string) (int, error) {
776 776
 	return 0, fmt.Errorf("No such container: %s", name)
777 777
 }
778 778
 
779
+func (srv *Server) ContainerResize(name string, h, w int) error {
780
+	if container := srv.runtime.Get(name); container != nil {
781
+		return container.Resize(h, w)
782
+	}
783
+	return fmt.Errorf("No such container: %s", name)
784
+}
785
+
779 786
 func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, stderr bool, in io.ReadCloser, out io.Writer) error {
780 787
 	container := srv.runtime.Get(name)
781 788
 	if container == nil {
... ...
@@ -85,6 +85,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
85 85
 		t.Fatal(err)
86 86
 	}
87 87
 
88
+	// FIXME: this failed once with a race condition ("Unable to remove filesystem for xxx: directory not empty")
88 89
 	if err = srv.ContainerDestroy(id, true); err != nil {
89 90
 		t.Fatal(err)
90 91
 	}
... ...
@@ -109,17 +109,35 @@ type State struct {
109 109
 	termios Termios
110 110
 }
111 111
 
112
+type Winsize struct {
113
+	Width  uint16
114
+	Height uint16
115
+	x      uint16
116
+	y      uint16
117
+}
118
+
119
+func GetWinsize(fd uintptr) (*Winsize, error) {
120
+	ws := &Winsize{}
121
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
122
+	return ws, err
123
+}
124
+
125
+func SetWinsize(fd uintptr, ws *Winsize) error {
126
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
127
+	return err
128
+}
129
+
112 130
 // IsTerminal returns true if the given file descriptor is a terminal.
113 131
 func IsTerminal(fd int) bool {
114 132
 	var termios Termios
115
-	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
133
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
116 134
 	return err == 0
117 135
 }
118 136
 
119 137
 // Restore restores the terminal connected to the given file descriptor to a
120 138
 // previous state.
121 139
 func Restore(fd int, state *State) error {
122
-	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
140
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
123 141
 	return err
124 142
 }
125 143
 
... ...
@@ -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,15 +85,15 @@ 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", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
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", r.readProgress, "?", "n/a")
89
+			fmt.Fprintf(r.output, r.template, r.readProgress, "?", "n/a")
90 90
 		}
91 91
 		r.lastUpdate = r.readProgress
92 92
 	}
93 93
 	// Send newline when complete
94 94
 	if err != nil {
95
-		fmt.Fprintf(r.output, "\n")
95
+		fmt.Fprintf(r.output, FormatStatus("", r.json))
96 96
 	}
97 97
 
98 98
 	return read, err
... ...
@@ -100,11 +101,11 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
100 100
 func (r *progressReader) Close() error {
101 101
 	return io.ReadCloser(r.reader).Close()
102 102
 }
103
-func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string) *progressReader {
104
-	if template == "" {
105
-		template = "%v/%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"
106 106
 	}
107
-	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template}
107
+	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template, json}
108 108
 }
109 109
 
110 110
 // HumanDuration returns a human-readable approximation of a duration
... ...
@@ -562,3 +563,19 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
562 562
 	}
563 563
 	return &WriteFlusher{w: w, flusher: flusher}
564 564
 }
565
+
566
+func FormatStatus(str string, json bool) string {
567
+	if json {
568
+		return "{\"status\" : \"" + str + "\"}"
569
+	}
570
+	return str + "\r\n"
571
+}
572
+
573
+func FormatProgress(str string, json bool) string {
574
+	if json {
575
+		return "{\"progress\" : \"" + str + "\"}"
576
+	}
577
+	return "Downloading " + str + "\r"
578
+}
579
+
580
+