Browse code

Merge branch 'master' into 691-run_id-feature

Victor Vieux authored on 2013/06/13 22:18:43
Showing 58 changed files
... ...
@@ -42,6 +42,7 @@ Ken Cochrane <kencochrane@gmail.com>
42 42
 Kevin J. Lynagh <kevin@keminglabs.com>
43 43
 Louis Opter <kalessin@kalessin.fr>
44 44
 Maxim Treskin <zerthurd@gmail.com>
45
+Michael Crosby <crosby.michael@gmail.com>
45 46
 Mikhail Sobolev <mss@mawhrin.net>
46 47
 Nate Jones <nate@endot.org>
47 48
 Nelson Chen <crazysim@gmail.com>
48 49
new file mode 100644
... ...
@@ -0,0 +1,18 @@
0
+
1
+## FIXME
2
+
3
+This file is a loose collection of things to improve in the codebase, for the internal
4
+use of the maintainers.
5
+
6
+They are not big enough to be in the roadmap, not user-facing enough to be github issues,
7
+and not important enough to be discussed in the mailing list.
8
+
9
+They are just like FIXME comments in the source code, except we're not sure where in the source
10
+to put them - so we put them here :)
11
+
12
+
13
+* Merge Runtime, Server and Builder into Runtime
14
+* Run linter on codebase
15
+* Unify build commands and regular commands
16
+* Move source code into src/ subdir for clarity
17
+* Clean up the Makefile, it's a mess
... ...
@@ -3,4 +3,9 @@ Copyright 2012-2013 dotCloud, inc.
3 3
 
4 4
 This product includes software developed at dotCloud, inc. (http://www.dotcloud.com).
5 5
 
6
-This product contains software (https://github.com/kr/pty) developed by Keith Rarick, licensed under the MIT License.
7 6
\ No newline at end of file
7
+This product contains software (https://github.com/kr/pty) developed by Keith Rarick, licensed under the MIT License.
8
+
9
+Transfers of Docker shall be in accordance with applicable export controls of any country and all other applicable
10
+legal requirements.  Docker shall not be distributed or downloaded to or in Cuba, Iran, North Korea, Sudan or Syria
11
+and shall not be distributed or downloaded to any person on the Denied Persons List administered by the U.S.
12
+Department of Commerce.
... ...
@@ -251,7 +251,7 @@ Note
251 251
 ----
252 252
 
253 253
 We also keep the documentation in this repository. The website documentation is generated using sphinx using these sources.
254
-Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/master/docs/README.md
254
+Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/tree/master/docs/README.md
255 255
 
256 256
 Please feel free to fix / update the documentation and send us pull requests. More tutorials are also welcome.
257 257
 
... ...
@@ -371,4 +371,10 @@ Standard Container Specification
371 371
 
372 372
 #### Security
373 373
 
374
+### Legal
375
+
376
+Transfers of Docker shall be in accordance with applicable export controls of any country and all other applicable
377
+legal requirements.  Docker shall not be distributed or downloaded to or in Cuba, Iran, North Korea, Sudan or Syria
378
+and shall not be distributed or downloaded to any person on the Denied Persons List administered by the U.S.
379
+Department of Commerce.
374 380
 
... ...
@@ -13,7 +13,7 @@ import (
13 13
 	"strings"
14 14
 )
15 15
 
16
-const API_VERSION = 1.1
16
+const APIVERSION = 1.1
17 17
 
18 18
 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
19 19
 	conn, _, err := w.(http.Hijacker).Hijack()
... ...
@@ -45,6 +45,8 @@ func httpError(w http.ResponseWriter, err error) {
45 45
 		http.Error(w, err.Error(), http.StatusNotFound)
46 46
 	} else if strings.HasPrefix(err.Error(), "Bad parameter") {
47 47
 		http.Error(w, err.Error(), http.StatusBadRequest)
48
+	} else if strings.HasPrefix(err.Error(), "Conflict") {
49
+		http.Error(w, err.Error(), http.StatusConflict)
48 50
 	} else if strings.HasPrefix(err.Error(), "Impossible") {
49 51
 		http.Error(w, err.Error(), http.StatusNotAcceptable)
50 52
 	} else {
... ...
@@ -52,7 +54,7 @@ func httpError(w http.ResponseWriter, err error) {
52 52
 	}
53 53
 }
54 54
 
55
-func writeJson(w http.ResponseWriter, b []byte) {
55
+func writeJSON(w http.ResponseWriter, b []byte) {
56 56
 	w.Header().Set("Content-Type", "application/json")
57 57
 	w.Write(b)
58 58
 }
... ...
@@ -82,7 +84,7 @@ func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
82 82
 	if err != nil {
83 83
 		return err
84 84
 	}
85
-	writeJson(w, b)
85
+	writeJSON(w, b)
86 86
 	return nil
87 87
 }
88 88
 
... ...
@@ -111,11 +113,11 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque
111 111
 	}
112 112
 
113 113
 	if status != "" {
114
-		b, err := json.Marshal(&ApiAuth{Status: status})
114
+		b, err := json.Marshal(&APIAuth{Status: status})
115 115
 		if err != nil {
116 116
 			return err
117 117
 		}
118
-		writeJson(w, b)
118
+		writeJSON(w, b)
119 119
 		return nil
120 120
 	}
121 121
 	w.WriteHeader(http.StatusNoContent)
... ...
@@ -128,7 +130,7 @@ func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Req
128 128
 	if err != nil {
129 129
 		return err
130 130
 	}
131
-	writeJson(w, b)
131
+	writeJSON(w, b)
132 132
 	return nil
133 133
 }
134 134
 
... ...
@@ -157,7 +159,7 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r
157 157
 	return nil
158 158
 }
159 159
 
160
-func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
160
+func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
161 161
 	if err := parseForm(r); err != nil {
162 162
 		return err
163 163
 	}
... ...
@@ -176,7 +178,7 @@ func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.
176 176
 	if err != nil {
177 177
 		return err
178 178
 	}
179
-	writeJson(w, b)
179
+	writeJSON(w, b)
180 180
 	return nil
181 181
 }
182 182
 
... ...
@@ -193,7 +195,7 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
193 193
 	if err != nil {
194 194
 		return err
195 195
 	}
196
-	writeJson(w, b)
196
+	writeJSON(w, b)
197 197
 	return nil
198 198
 }
199 199
 
... ...
@@ -210,7 +212,7 @@ func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *ht
210 210
 	if err != nil {
211 211
 		return err
212 212
 	}
213
-	writeJson(w, b)
213
+	writeJSON(w, b)
214 214
 	return nil
215 215
 }
216 216
 
... ...
@@ -227,11 +229,11 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r
227 227
 	if err != nil {
228 228
 		return err
229 229
 	}
230
-	writeJson(w, b)
230
+	writeJSON(w, b)
231 231
 	return nil
232 232
 }
233 233
 
234
-func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
234
+func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
235 235
 	if err := parseForm(r); err != nil {
236 236
 		return err
237 237
 	}
... ...
@@ -251,7 +253,7 @@ func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *h
251 251
 	if err != nil {
252 252
 		return err
253 253
 	}
254
-	writeJson(w, b)
254
+	writeJSON(w, b)
255 255
 	return nil
256 256
 }
257 257
 
... ...
@@ -294,12 +296,12 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req
294 294
 	if err != nil {
295 295
 		return err
296 296
 	}
297
-	b, err := json.Marshal(&ApiId{id})
297
+	b, err := json.Marshal(&APIID{id})
298 298
 	if err != nil {
299 299
 		return err
300 300
 	}
301 301
 	w.WriteHeader(http.StatusCreated)
302
-	writeJson(w, b)
302
+	writeJSON(w, b)
303 303
 	return nil
304 304
 }
305 305
 
... ...
@@ -353,7 +355,7 @@ func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *htt
353 353
 	if err != nil {
354 354
 		return err
355 355
 	}
356
-	writeJson(w, b)
356
+	writeJSON(w, b)
357 357
 	return nil
358 358
 }
359 359
 
... ...
@@ -372,18 +374,18 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
372 372
 		w.Header().Set("Content-Type", "application/json")
373 373
 	}
374 374
 	sf := utils.NewStreamFormatter(version > 1.0)
375
-	imgId, err := srv.ImageInsert(name, url, path, w, sf)
375
+	imgID, err := srv.ImageInsert(name, url, path, w, sf)
376 376
 	if err != nil {
377 377
 		if sf.Used() {
378 378
 			w.Write(sf.FormatError(err))
379 379
 			return nil
380 380
 		}
381 381
 	}
382
-	b, err := json.Marshal(&ApiId{Id: imgId})
382
+	b, err := json.Marshal(&APIID{ID: imgID})
383 383
 	if err != nil {
384 384
 		return err
385 385
 	}
386
-	writeJson(w, b)
386
+	writeJSON(w, b)
387 387
 	return nil
388 388
 }
389 389
 
... ...
@@ -421,8 +423,8 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
421 421
 		return err
422 422
 	}
423 423
 
424
-	out := &ApiRun{
425
-		Id: id,
424
+	out := &APIRun{
425
+		ID: id,
426 426
 	}
427 427
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
428 428
 		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
... ...
@@ -437,7 +439,7 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
437 437
 		return err
438 438
 	}
439 439
 	w.WriteHeader(http.StatusCreated)
440
-	writeJson(w, b)
440
+	writeJSON(w, b)
441 441
 	return nil
442 442
 }
443 443
 
... ...
@@ -481,14 +483,30 @@ func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *ht
481 481
 }
482 482
 
483 483
 func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
484
+	if err := parseForm(r); err != nil {
485
+		return err
486
+	}
484 487
 	if vars == nil {
485 488
 		return fmt.Errorf("Missing parameter")
486 489
 	}
487 490
 	name := vars["name"]
488
-	if err := srv.ImageDelete(name); err != nil {
491
+	imgs, err := srv.ImageDelete(name, version > 1.0)
492
+	if err != nil {
489 493
 		return err
490 494
 	}
491
-	w.WriteHeader(http.StatusNoContent)
495
+	if imgs != nil {
496
+		if len(*imgs) != 0 {
497
+			b, err := json.Marshal(imgs)
498
+			if err != nil {
499
+				return err
500
+			}
501
+			writeJSON(w, b)
502
+		} else {
503
+			return fmt.Errorf("Conflict, %s wasn't deleted", name)
504
+		}
505
+	} else {
506
+		w.WriteHeader(http.StatusNoContent)
507
+	}
492 508
 	return nil
493 509
 }
494 510
 
... ...
@@ -534,11 +552,11 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
534 534
 	if err != nil {
535 535
 		return err
536 536
 	}
537
-	b, err := json.Marshal(&ApiWait{StatusCode: status})
537
+	b, err := json.Marshal(&APIWait{StatusCode: status})
538 538
 	if err != nil {
539 539
 		return err
540 540
 	}
541
-	writeJson(w, b)
541
+	writeJSON(w, b)
542 542
 	return nil
543 543
 }
544 544
 
... ...
@@ -625,7 +643,7 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r
625 625
 	if err != nil {
626 626
 		return err
627 627
 	}
628
-	writeJson(w, b)
628
+	writeJSON(w, b)
629 629
 	return nil
630 630
 }
631 631
 
... ...
@@ -643,17 +661,17 @@ func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *htt
643 643
 	if err != nil {
644 644
 		return err
645 645
 	}
646
-	writeJson(w, b)
646
+	writeJSON(w, b)
647 647
 	return nil
648 648
 }
649 649
 
650 650
 func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
651
-	apiConfig := &ApiImageConfig{}
651
+	apiConfig := &APIImageConfig{}
652 652
 	if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
653 653
 		return err
654 654
 	}
655 655
 
656
-	image, err := srv.ImageGetCached(apiConfig.Id, apiConfig.Config)
656
+	image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config)
657 657
 	if err != nil {
658 658
 		return err
659 659
 	}
... ...
@@ -661,12 +679,12 @@ func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *
661 661
 		w.WriteHeader(http.StatusNotFound)
662 662
 		return nil
663 663
 	}
664
-	apiId := &ApiId{Id: image.Id}
665
-	b, err := json.Marshal(apiId)
664
+	apiID := &APIID{ID: image.ID}
665
+	b, err := json.Marshal(apiID)
666 666
 	if err != nil {
667 667
 		return err
668 668
 	}
669
-	writeJson(w, b)
669
+	writeJSON(w, b)
670 670
 	return nil
671 671
 }
672 672
 
... ...
@@ -703,22 +721,31 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
703 703
 	return nil
704 704
 }
705 705
 
706
-func ListenAndServe(addr string, srv *Server, logging bool) error {
706
+func optionsHandler(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
707
+	w.WriteHeader(http.StatusOK)
708
+	return nil
709
+}
710
+func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
711
+	w.Header().Add("Access-Control-Allow-Origin", "*")
712
+	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
713
+	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
714
+}
715
+
716
+func createRouter(srv *Server, logging bool) (*mux.Router, error) {
707 717
 	r := mux.NewRouter()
708
-	log.Printf("Listening for HTTP on %s\n", addr)
709 718
 
710 719
 	m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{
711 720
 		"GET": {
712 721
 			"/auth":                         getAuth,
713 722
 			"/version":                      getVersion,
714 723
 			"/info":                         getInfo,
715
-			"/images/json":                  getImagesJson,
724
+			"/images/json":                  getImagesJSON,
716 725
 			"/images/viz":                   getImagesViz,
717 726
 			"/images/search":                getImagesSearch,
718 727
 			"/images/{name:.*}/history":     getImagesHistory,
719 728
 			"/images/{name:.*}/json":        getImagesByName,
720
-			"/containers/ps":                getContainersJson,
721
-			"/containers/json":              getContainersJson,
729
+			"/containers/ps":                getContainersJSON,
730
+			"/containers/json":              getContainersJSON,
722 731
 			"/containers/{name:.*}/export":  getContainersExport,
723 732
 			"/containers/{name:.*}/changes": getContainersChanges,
724 733
 			"/containers/{name:.*}/json":    getContainersByName,
... ...
@@ -745,6 +772,9 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
745 745
 			"/containers/{name:.*}": deleteContainers,
746 746
 			"/images/{name:.*}":     deleteImages,
747 747
 		},
748
+		"OPTIONS": {
749
+			"": optionsHandler,
750
+		},
748 751
 	}
749 752
 
750 753
 	for method, routes := range m {
... ...
@@ -767,9 +797,12 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
767 767
 				}
768 768
 				version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
769 769
 				if err != nil {
770
-					version = API_VERSION
770
+					version = APIVERSION
771 771
 				}
772
-				if version == 0 || version > API_VERSION {
772
+				if srv.enableCors {
773
+					writeCorsHeaders(w, r)
774
+				}
775
+				if version == 0 || version > APIVERSION {
773 776
 					w.WriteHeader(http.StatusNotFound)
774 777
 					return
775 778
 				}
... ...
@@ -777,9 +810,24 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
777 777
 					httpError(w, err)
778 778
 				}
779 779
 			}
780
-			r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
781
-			r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
780
+
781
+			if localRoute == "" {
782
+				r.Methods(localMethod).HandlerFunc(f)
783
+			} else {
784
+				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
785
+				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
786
+			}
782 787
 		}
783 788
 	}
789
+	return r, nil
790
+}
791
+
792
+func ListenAndServe(addr string, srv *Server, logging bool) error {
793
+	log.Printf("Listening for HTTP on %s\n", addr)
794
+
795
+	r, err := createRouter(srv, logging)
796
+	if err != nil {
797
+		return err
798
+	}
784 799
 	return http.ListenAndServe(addr, r)
785 800
 }
... ...
@@ -1,19 +1,19 @@
1 1
 package docker
2 2
 
3
-type ApiHistory struct {
4
-	Id        string
3
+type APIHistory struct {
4
+	ID        string `json:"Id"`
5 5
 	Created   int64
6 6
 	CreatedBy string `json:",omitempty"`
7 7
 }
8 8
 
9
-type ApiImages struct {
9
+type APIImages struct {
10 10
 	Repository string `json:",omitempty"`
11 11
 	Tag        string `json:",omitempty"`
12
-	Id         string
12
+	ID         string `json:"Id"`
13 13
 	Created    int64
14 14
 }
15 15
 
16
-type ApiInfo struct {
16
+type APIInfo struct {
17 17
 	Debug       bool
18 18
 	Containers  int
19 19
 	Images      int
... ...
@@ -23,8 +23,13 @@ type ApiInfo struct {
23 23
 	SwapLimit   bool `json:",omitempty"`
24 24
 }
25 25
 
26
-type ApiContainers struct {
27
-	Id      string
26
+type APIRmi struct {
27
+	Deleted  string `json:",omitempty"`
28
+	Untagged string `json:",omitempty"`
29
+}
30
+
31
+type APIContainers struct {
32
+	ID      string `json:"Id"`
28 33
 	Image   string
29 34
 	Command string
30 35
 	Created int64
... ...
@@ -32,39 +37,39 @@ type ApiContainers struct {
32 32
 	Ports   string
33 33
 }
34 34
 
35
-type ApiSearch struct {
35
+type APISearch struct {
36 36
 	Name        string
37 37
 	Description string
38 38
 }
39 39
 
40
-type ApiId struct {
41
-	Id string
40
+type APIID struct {
41
+	ID string `json:"Id"`
42 42
 }
43 43
 
44
-type ApiRun struct {
45
-	Id       string
44
+type APIRun struct {
45
+	ID       string   `json:"Id"`
46 46
 	Warnings []string `json:",omitempty"`
47 47
 }
48 48
 
49
-type ApiPort struct {
49
+type APIPort struct {
50 50
 	Port string
51 51
 }
52 52
 
53
-type ApiVersion struct {
53
+type APIVersion struct {
54 54
 	Version   string
55 55
 	GitCommit string `json:",omitempty"`
56 56
 	GoVersion string `json:",omitempty"`
57 57
 }
58 58
 
59
-type ApiWait struct {
59
+type APIWait struct {
60 60
 	StatusCode int
61 61
 }
62 62
 
63
-type ApiAuth struct {
63
+type APIAuth struct {
64 64
 	Status string
65 65
 }
66 66
 
67
-type ApiImageConfig struct {
68
-	Id string
67
+type APIImageConfig struct {
68
+	ID string `json:"Id"`
69 69
 	*Config
70 70
 }
... ...
@@ -37,17 +37,17 @@ func TestGetAuth(t *testing.T) {
37 37
 		Email:    "utest@yopmail.com",
38 38
 	}
39 39
 
40
-	authConfigJson, err := json.Marshal(authConfig)
40
+	authConfigJSON, err := json.Marshal(authConfig)
41 41
 	if err != nil {
42 42
 		t.Fatal(err)
43 43
 	}
44 44
 
45
-	req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJson))
45
+	req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJSON))
46 46
 	if err != nil {
47 47
 		t.Fatal(err)
48 48
 	}
49 49
 
50
-	if err := postAuth(srv, API_VERSION, r, req, nil); err != nil {
50
+	if err := postAuth(srv, APIVERSION, r, req, nil); err != nil {
51 51
 		t.Fatal(err)
52 52
 	}
53 53
 
... ...
@@ -73,11 +73,11 @@ func TestGetVersion(t *testing.T) {
73 73
 
74 74
 	r := httptest.NewRecorder()
75 75
 
76
-	if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil {
76
+	if err := getVersion(srv, APIVERSION, r, nil, nil); err != nil {
77 77
 		t.Fatal(err)
78 78
 	}
79 79
 
80
-	v := &ApiVersion{}
80
+	v := &APIVersion{}
81 81
 	if err = json.Unmarshal(r.Body.Bytes(), v); err != nil {
82 82
 		t.Fatal(err)
83 83
 	}
... ...
@@ -97,11 +97,11 @@ func TestGetInfo(t *testing.T) {
97 97
 
98 98
 	r := httptest.NewRecorder()
99 99
 
100
-	if err := getInfo(srv, API_VERSION, r, nil, nil); err != nil {
100
+	if err := getInfo(srv, APIVERSION, r, nil, nil); err != nil {
101 101
 		t.Fatal(err)
102 102
 	}
103 103
 
104
-	infos := &ApiInfo{}
104
+	infos := &APIInfo{}
105 105
 	err = json.Unmarshal(r.Body.Bytes(), infos)
106 106
 	if err != nil {
107 107
 		t.Fatal(err)
... ...
@@ -111,7 +111,7 @@ func TestGetInfo(t *testing.T) {
111 111
 	}
112 112
 }
113 113
 
114
-func TestGetImagesJson(t *testing.T) {
114
+func TestGetImagesJSON(t *testing.T) {
115 115
 	runtime, err := newTestRuntime()
116 116
 	if err != nil {
117 117
 		t.Fatal(err)
... ...
@@ -128,11 +128,11 @@ func TestGetImagesJson(t *testing.T) {
128 128
 
129 129
 	r := httptest.NewRecorder()
130 130
 
131
-	if err := getImagesJson(srv, API_VERSION, r, req, nil); err != nil {
131
+	if err := getImagesJSON(srv, APIVERSION, r, req, nil); err != nil {
132 132
 		t.Fatal(err)
133 133
 	}
134 134
 
135
-	images := []ApiImages{}
135
+	images := []APIImages{}
136 136
 	if err := json.Unmarshal(r.Body.Bytes(), &images); err != nil {
137 137
 		t.Fatal(err)
138 138
 	}
... ...
@@ -153,11 +153,11 @@ func TestGetImagesJson(t *testing.T) {
153 153
 		t.Fatal(err)
154 154
 	}
155 155
 
156
-	if err := getImagesJson(srv, API_VERSION, r2, req2, nil); err != nil {
156
+	if err := getImagesJSON(srv, APIVERSION, r2, req2, nil); err != nil {
157 157
 		t.Fatal(err)
158 158
 	}
159 159
 
160
-	images2 := []ApiImages{}
160
+	images2 := []APIImages{}
161 161
 	if err := json.Unmarshal(r2.Body.Bytes(), &images2); err != nil {
162 162
 		t.Fatal(err)
163 163
 	}
... ...
@@ -166,8 +166,8 @@ func TestGetImagesJson(t *testing.T) {
166 166
 		t.Errorf("Excepted 1 image, %d found", len(images2))
167 167
 	}
168 168
 
169
-	if images2[0].Id != GetTestImage(runtime).Id {
170
-		t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).Id, images2[0].Id)
169
+	if images2[0].ID != GetTestImage(runtime).ID {
170
+		t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).ID, images2[0].ID)
171 171
 	}
172 172
 
173 173
 	r3 := httptest.NewRecorder()
... ...
@@ -178,11 +178,11 @@ func TestGetImagesJson(t *testing.T) {
178 178
 		t.Fatal(err)
179 179
 	}
180 180
 
181
-	if err := getImagesJson(srv, API_VERSION, r3, req3, nil); err != nil {
181
+	if err := getImagesJSON(srv, APIVERSION, r3, req3, nil); err != nil {
182 182
 		t.Fatal(err)
183 183
 	}
184 184
 
185
-	images3 := []ApiImages{}
185
+	images3 := []APIImages{}
186 186
 	if err := json.Unmarshal(r3.Body.Bytes(), &images3); err != nil {
187 187
 		t.Fatal(err)
188 188
 	}
... ...
@@ -199,7 +199,7 @@ func TestGetImagesJson(t *testing.T) {
199 199
 		t.Fatal(err)
200 200
 	}
201 201
 
202
-	err = getImagesJson(srv, API_VERSION, r4, req4, nil)
202
+	err = getImagesJSON(srv, APIVERSION, r4, req4, nil)
203 203
 	if err == nil {
204 204
 		t.Fatalf("Error expected, received none")
205 205
 	}
... ...
@@ -220,7 +220,7 @@ func TestGetImagesViz(t *testing.T) {
220 220
 	srv := &Server{runtime: runtime}
221 221
 
222 222
 	r := httptest.NewRecorder()
223
-	if err := getImagesViz(srv, API_VERSION, r, nil, nil); err != nil {
223
+	if err := getImagesViz(srv, APIVERSION, r, nil, nil); err != nil {
224 224
 		t.Fatal(err)
225 225
 	}
226 226
 
... ...
@@ -256,11 +256,11 @@ func TestGetImagesSearch(t *testing.T) {
256 256
 		t.Fatal(err)
257 257
 	}
258 258
 
259
-	if err := getImagesSearch(srv, API_VERSION, r, req, nil); err != nil {
259
+	if err := getImagesSearch(srv, APIVERSION, r, req, nil); err != nil {
260 260
 		t.Fatal(err)
261 261
 	}
262 262
 
263
-	results := []ApiSearch{}
263
+	results := []APISearch{}
264 264
 	if err := json.Unmarshal(r.Body.Bytes(), &results); err != nil {
265 265
 		t.Fatal(err)
266 266
 	}
... ...
@@ -280,11 +280,11 @@ func TestGetImagesHistory(t *testing.T) {
280 280
 
281 281
 	r := httptest.NewRecorder()
282 282
 
283
-	if err := getImagesHistory(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
283
+	if err := getImagesHistory(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
284 284
 		t.Fatal(err)
285 285
 	}
286 286
 
287
-	history := []ApiHistory{}
287
+	history := []APIHistory{}
288 288
 	if err := json.Unmarshal(r.Body.Bytes(), &history); err != nil {
289 289
 		t.Fatal(err)
290 290
 	}
... ...
@@ -303,7 +303,7 @@ func TestGetImagesByName(t *testing.T) {
303 303
 	srv := &Server{runtime: runtime}
304 304
 
305 305
 	r := httptest.NewRecorder()
306
-	if err := getImagesByName(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
306
+	if err := getImagesByName(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
307 307
 		t.Fatal(err)
308 308
 	}
309 309
 
... ...
@@ -311,12 +311,12 @@ func TestGetImagesByName(t *testing.T) {
311 311
 	if err := json.Unmarshal(r.Body.Bytes(), img); err != nil {
312 312
 		t.Fatal(err)
313 313
 	}
314
-	if img.Id != GetTestImage(runtime).Id || img.Comment != "Imported from http://get.docker.io/images/busybox" {
314
+	if img.ID != GetTestImage(runtime).ID || img.Comment != "Imported from http://get.docker.io/images/busybox" {
315 315
 		t.Errorf("Error inspecting image")
316 316
 	}
317 317
 }
318 318
 
319
-func TestGetContainersJson(t *testing.T) {
319
+func TestGetContainersJSON(t *testing.T) {
320 320
 	runtime, err := newTestRuntime()
321 321
 	if err != nil {
322 322
 		t.Fatal(err)
... ...
@@ -326,7 +326,7 @@ func TestGetContainersJson(t *testing.T) {
326 326
 	srv := &Server{runtime: runtime}
327 327
 
328 328
 	container, err := NewBuilder(runtime).Create(&Config{
329
-		Image: GetTestImage(runtime).Id,
329
+		Image: GetTestImage(runtime).ID,
330 330
 		Cmd:   []string{"echo", "test"},
331 331
 	})
332 332
 	if err != nil {
... ...
@@ -340,18 +340,18 @@ func TestGetContainersJson(t *testing.T) {
340 340
 	}
341 341
 
342 342
 	r := httptest.NewRecorder()
343
-	if err := getContainersJson(srv, API_VERSION, r, req, nil); err != nil {
343
+	if err := getContainersJSON(srv, APIVERSION, r, req, nil); err != nil {
344 344
 		t.Fatal(err)
345 345
 	}
346
-	containers := []ApiContainers{}
346
+	containers := []APIContainers{}
347 347
 	if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
348 348
 		t.Fatal(err)
349 349
 	}
350 350
 	if len(containers) != 1 {
351 351
 		t.Fatalf("Excepted %d container, %d found", 1, len(containers))
352 352
 	}
353
-	if containers[0].Id != container.Id {
354
-		t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.Id, containers[0].Id)
353
+	if containers[0].ID != container.ID {
354
+		t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.ID, containers[0].ID)
355 355
 	}
356 356
 }
357 357
 
... ...
@@ -369,7 +369,7 @@ func TestGetContainersExport(t *testing.T) {
369 369
 	// Create a container and remove a file
370 370
 	container, err := builder.Create(
371 371
 		&Config{
372
-			Image: GetTestImage(runtime).Id,
372
+			Image: GetTestImage(runtime).ID,
373 373
 			Cmd:   []string{"touch", "/test"},
374 374
 		},
375 375
 	)
... ...
@@ -383,7 +383,7 @@ func TestGetContainersExport(t *testing.T) {
383 383
 	}
384 384
 
385 385
 	r := httptest.NewRecorder()
386
-	if err = getContainersExport(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
386
+	if err = getContainersExport(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
387 387
 		t.Fatal(err)
388 388
 	}
389 389
 
... ...
@@ -424,7 +424,7 @@ func TestGetContainersChanges(t *testing.T) {
424 424
 	// Create a container and remove a file
425 425
 	container, err := builder.Create(
426 426
 		&Config{
427
-			Image: GetTestImage(runtime).Id,
427
+			Image: GetTestImage(runtime).ID,
428 428
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
429 429
 		},
430 430
 	)
... ...
@@ -438,7 +438,7 @@ func TestGetContainersChanges(t *testing.T) {
438 438
 	}
439 439
 
440 440
 	r := httptest.NewRecorder()
441
-	if err := getContainersChanges(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
441
+	if err := getContainersChanges(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
442 442
 		t.Fatal(err)
443 443
 	}
444 444
 	changes := []Change{}
... ...
@@ -472,7 +472,7 @@ func TestGetContainersByName(t *testing.T) {
472 472
 	// Create a container and remove a file
473 473
 	container, err := builder.Create(
474 474
 		&Config{
475
-			Image: GetTestImage(runtime).Id,
475
+			Image: GetTestImage(runtime).ID,
476 476
 			Cmd:   []string{"echo", "test"},
477 477
 		},
478 478
 	)
... ...
@@ -482,15 +482,15 @@ func TestGetContainersByName(t *testing.T) {
482 482
 	defer runtime.Destroy(container)
483 483
 
484 484
 	r := httptest.NewRecorder()
485
-	if err := getContainersByName(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
485
+	if err := getContainersByName(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
486 486
 		t.Fatal(err)
487 487
 	}
488 488
 	outContainer := &Container{}
489 489
 	if err := json.Unmarshal(r.Body.Bytes(), outContainer); err != nil {
490 490
 		t.Fatal(err)
491 491
 	}
492
-	if outContainer.Id != container.Id {
493
-		t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.Id, outContainer.Id)
492
+	if outContainer.ID != container.ID {
493
+		t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.ID, outContainer.ID)
494 494
 	}
495 495
 }
496 496
 
... ...
@@ -514,7 +514,7 @@ func TestPostAuth(t *testing.T) {
514 514
 	auth.SaveConfig(runtime.root, authStr, config.Email)
515 515
 
516 516
 	r := httptest.NewRecorder()
517
-	if err := getAuth(srv, API_VERSION, r, nil, nil); err != nil {
517
+	if err := getAuth(srv, APIVERSION, r, nil, nil); err != nil {
518 518
 		t.Fatal(err)
519 519
 	}
520 520
 
... ...
@@ -542,7 +542,7 @@ func TestPostCommit(t *testing.T) {
542 542
 	// Create a container and remove a file
543 543
 	container, err := builder.Create(
544 544
 		&Config{
545
-			Image: GetTestImage(runtime).Id,
545
+			Image: GetTestImage(runtime).ID,
546 546
 			Cmd:   []string{"touch", "/test"},
547 547
 		},
548 548
 	)
... ...
@@ -555,24 +555,24 @@ func TestPostCommit(t *testing.T) {
555 555
 		t.Fatal(err)
556 556
 	}
557 557
 
558
-	req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.Id, bytes.NewReader([]byte{}))
558
+	req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.ID, bytes.NewReader([]byte{}))
559 559
 	if err != nil {
560 560
 		t.Fatal(err)
561 561
 	}
562 562
 
563 563
 	r := httptest.NewRecorder()
564
-	if err := postCommit(srv, API_VERSION, r, req, nil); err != nil {
564
+	if err := postCommit(srv, APIVERSION, r, req, nil); err != nil {
565 565
 		t.Fatal(err)
566 566
 	}
567 567
 	if r.Code != http.StatusCreated {
568 568
 		t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
569 569
 	}
570 570
 
571
-	apiId := &ApiId{}
572
-	if err := json.Unmarshal(r.Body.Bytes(), apiId); err != nil {
571
+	apiID := &APIID{}
572
+	if err := json.Unmarshal(r.Body.Bytes(), apiID); err != nil {
573 573
 		t.Fatal(err)
574 574
 	}
575
-	if _, err := runtime.graph.Get(apiId.Id); err != nil {
575
+	if _, err := runtime.graph.Get(apiID.ID); err != nil {
576 576
 		t.Fatalf("The image has not been commited")
577 577
 	}
578 578
 }
... ...
@@ -715,7 +715,7 @@ func TestPostImagesInsert(t *testing.T) {
715 715
 	// 	t.Fatalf("The test file has not been found")
716 716
 	// }
717 717
 
718
-	// if err := srv.runtime.graph.Delete(img.Id); err != nil {
718
+	// if err := srv.runtime.graph.Delete(img.ID); err != nil {
719 719
 	// 	t.Fatal(err)
720 720
 	// }
721 721
 }
... ...
@@ -824,8 +824,8 @@ func TestPostContainersCreate(t *testing.T) {
824 824
 
825 825
 	srv := &Server{runtime: runtime}
826 826
 
827
-	configJson, err := json.Marshal(&Config{
828
-		Image:  GetTestImage(runtime).Id,
827
+	configJSON, err := json.Marshal(&Config{
828
+		Image:  GetTestImage(runtime).ID,
829 829
 		Memory: 33554432,
830 830
 		Cmd:    []string{"touch", "/test"},
831 831
 	})
... ...
@@ -833,25 +833,25 @@ func TestPostContainersCreate(t *testing.T) {
833 833
 		t.Fatal(err)
834 834
 	}
835 835
 
836
-	req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJson))
836
+	req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJSON))
837 837
 	if err != nil {
838 838
 		t.Fatal(err)
839 839
 	}
840 840
 
841 841
 	r := httptest.NewRecorder()
842
-	if err := postContainersCreate(srv, API_VERSION, r, req, nil); err != nil {
842
+	if err := postContainersCreate(srv, APIVERSION, r, req, nil); err != nil {
843 843
 		t.Fatal(err)
844 844
 	}
845 845
 	if r.Code != http.StatusCreated {
846 846
 		t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
847 847
 	}
848 848
 
849
-	apiRun := &ApiRun{}
849
+	apiRun := &APIRun{}
850 850
 	if err := json.Unmarshal(r.Body.Bytes(), apiRun); err != nil {
851 851
 		t.Fatal(err)
852 852
 	}
853 853
 
854
-	container := srv.runtime.Get(apiRun.Id)
854
+	container := srv.runtime.Get(apiRun.ID)
855 855
 	if container == nil {
856 856
 		t.Fatalf("Container not created")
857 857
 	}
... ...
@@ -880,7 +880,7 @@ func TestPostContainersKill(t *testing.T) {
880 880
 
881 881
 	container, err := NewBuilder(runtime).Create(
882 882
 		&Config{
883
-			Image:     GetTestImage(runtime).Id,
883
+			Image:     GetTestImage(runtime).ID,
884 884
 			Cmd:       []string{"/bin/cat"},
885 885
 			OpenStdin: true,
886 886
 		},
... ...
@@ -902,7 +902,7 @@ func TestPostContainersKill(t *testing.T) {
902 902
 	}
903 903
 
904 904
 	r := httptest.NewRecorder()
905
-	if err := postContainersKill(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
905
+	if err := postContainersKill(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
906 906
 		t.Fatal(err)
907 907
 	}
908 908
 	if r.Code != http.StatusNoContent {
... ...
@@ -924,7 +924,7 @@ func TestPostContainersRestart(t *testing.T) {
924 924
 
925 925
 	container, err := NewBuilder(runtime).Create(
926 926
 		&Config{
927
-			Image:     GetTestImage(runtime).Id,
927
+			Image:     GetTestImage(runtime).ID,
928 928
 			Cmd:       []string{"/bin/cat"},
929 929
 			OpenStdin: true,
930 930
 		},
... ...
@@ -945,12 +945,12 @@ func TestPostContainersRestart(t *testing.T) {
945 945
 		t.Errorf("Container should be running")
946 946
 	}
947 947
 
948
-	req, err := http.NewRequest("POST", "/containers/"+container.Id+"/restart?t=1", bytes.NewReader([]byte{}))
948
+	req, err := http.NewRequest("POST", "/containers/"+container.ID+"/restart?t=1", bytes.NewReader([]byte{}))
949 949
 	if err != nil {
950 950
 		t.Fatal(err)
951 951
 	}
952 952
 	r := httptest.NewRecorder()
953
-	if err := postContainersRestart(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
953
+	if err := postContainersRestart(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
954 954
 		t.Fatal(err)
955 955
 	}
956 956
 	if r.Code != http.StatusNoContent {
... ...
@@ -980,7 +980,7 @@ func TestPostContainersStart(t *testing.T) {
980 980
 
981 981
 	container, err := NewBuilder(runtime).Create(
982 982
 		&Config{
983
-			Image:     GetTestImage(runtime).Id,
983
+			Image:     GetTestImage(runtime).ID,
984 984
 			Cmd:       []string{"/bin/cat"},
985 985
 			OpenStdin: true,
986 986
 		},
... ...
@@ -991,7 +991,7 @@ func TestPostContainersStart(t *testing.T) {
991 991
 	defer runtime.Destroy(container)
992 992
 
993 993
 	r := httptest.NewRecorder()
994
-	if err := postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
994
+	if err := postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
995 995
 		t.Fatal(err)
996 996
 	}
997 997
 	if r.Code != http.StatusNoContent {
... ...
@@ -1006,7 +1006,7 @@ func TestPostContainersStart(t *testing.T) {
1006 1006
 	}
1007 1007
 
1008 1008
 	r = httptest.NewRecorder()
1009
-	if err = postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err == nil {
1009
+	if err = postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err == nil {
1010 1010
 		t.Fatalf("A running containter should be able to be started")
1011 1011
 	}
1012 1012
 
... ...
@@ -1026,7 +1026,7 @@ func TestPostContainersStop(t *testing.T) {
1026 1026
 
1027 1027
 	container, err := NewBuilder(runtime).Create(
1028 1028
 		&Config{
1029
-			Image:     GetTestImage(runtime).Id,
1029
+			Image:     GetTestImage(runtime).ID,
1030 1030
 			Cmd:       []string{"/bin/cat"},
1031 1031
 			OpenStdin: true,
1032 1032
 		},
... ...
@@ -1048,12 +1048,12 @@ func TestPostContainersStop(t *testing.T) {
1048 1048
 	}
1049 1049
 
1050 1050
 	// Note: as it is a POST request, it requires a body.
1051
-	req, err := http.NewRequest("POST", "/containers/"+container.Id+"/stop?t=1", bytes.NewReader([]byte{}))
1051
+	req, err := http.NewRequest("POST", "/containers/"+container.ID+"/stop?t=1", bytes.NewReader([]byte{}))
1052 1052
 	if err != nil {
1053 1053
 		t.Fatal(err)
1054 1054
 	}
1055 1055
 	r := httptest.NewRecorder()
1056
-	if err := postContainersStop(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
1056
+	if err := postContainersStop(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
1057 1057
 		t.Fatal(err)
1058 1058
 	}
1059 1059
 	if r.Code != http.StatusNoContent {
... ...
@@ -1075,7 +1075,7 @@ func TestPostContainersWait(t *testing.T) {
1075 1075
 
1076 1076
 	container, err := NewBuilder(runtime).Create(
1077 1077
 		&Config{
1078
-			Image:     GetTestImage(runtime).Id,
1078
+			Image:     GetTestImage(runtime).ID,
1079 1079
 			Cmd:       []string{"/bin/sleep", "1"},
1080 1080
 			OpenStdin: true,
1081 1081
 		},
... ...
@@ -1091,10 +1091,10 @@ func TestPostContainersWait(t *testing.T) {
1091 1091
 
1092 1092
 	setTimeout(t, "Wait timed out", 3*time.Second, func() {
1093 1093
 		r := httptest.NewRecorder()
1094
-		if err := postContainersWait(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
1094
+		if err := postContainersWait(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
1095 1095
 			t.Fatal(err)
1096 1096
 		}
1097
-		apiWait := &ApiWait{}
1097
+		apiWait := &APIWait{}
1098 1098
 		if err := json.Unmarshal(r.Body.Bytes(), apiWait); err != nil {
1099 1099
 			t.Fatal(err)
1100 1100
 		}
... ...
@@ -1119,7 +1119,7 @@ func TestPostContainersAttach(t *testing.T) {
1119 1119
 
1120 1120
 	container, err := NewBuilder(runtime).Create(
1121 1121
 		&Config{
1122
-			Image:     GetTestImage(runtime).Id,
1122
+			Image:     GetTestImage(runtime).ID,
1123 1123
 			Cmd:       []string{"/bin/cat"},
1124 1124
 			OpenStdin: true,
1125 1125
 		},
... ...
@@ -1148,12 +1148,12 @@ func TestPostContainersAttach(t *testing.T) {
1148 1148
 			out:              stdoutPipe,
1149 1149
 		}
1150 1150
 
1151
-		req, err := http.NewRequest("POST", "/containers/"+container.Id+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
1151
+		req, err := http.NewRequest("POST", "/containers/"+container.ID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
1152 1152
 		if err != nil {
1153 1153
 			t.Fatal(err)
1154 1154
 		}
1155 1155
 
1156
-		if err := postContainersAttach(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
1156
+		if err := postContainersAttach(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
1157 1157
 			t.Fatal(err)
1158 1158
 		}
1159 1159
 	}()
... ...
@@ -1206,7 +1206,7 @@ func TestDeleteContainers(t *testing.T) {
1206 1206
 	srv := &Server{runtime: runtime}
1207 1207
 
1208 1208
 	container, err := NewBuilder(runtime).Create(&Config{
1209
-		Image: GetTestImage(runtime).Id,
1209
+		Image: GetTestImage(runtime).ID,
1210 1210
 		Cmd:   []string{"touch", "/test"},
1211 1211
 	})
1212 1212
 	if err != nil {
... ...
@@ -1218,19 +1218,19 @@ func TestDeleteContainers(t *testing.T) {
1218 1218
 		t.Fatal(err)
1219 1219
 	}
1220 1220
 
1221
-	req, err := http.NewRequest("DELETE", "/containers/"+container.Id, nil)
1221
+	req, err := http.NewRequest("DELETE", "/containers/"+container.ID, nil)
1222 1222
 	if err != nil {
1223 1223
 		t.Fatal(err)
1224 1224
 	}
1225 1225
 	r := httptest.NewRecorder()
1226
-	if err := deleteContainers(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
1226
+	if err := deleteContainers(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
1227 1227
 		t.Fatal(err)
1228 1228
 	}
1229 1229
 	if r.Code != http.StatusNoContent {
1230 1230
 		t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
1231 1231
 	}
1232 1232
 
1233
-	if c := runtime.Get(container.Id); c != nil {
1233
+	if c := runtime.Get(container.ID); c != nil {
1234 1234
 		t.Fatalf("The container as not been deleted")
1235 1235
 	}
1236 1236
 
... ...
@@ -1239,9 +1239,131 @@ func TestDeleteContainers(t *testing.T) {
1239 1239
 	}
1240 1240
 }
1241 1241
 
1242
+func TestOptionsRoute(t *testing.T) {
1243
+	runtime, err := newTestRuntime()
1244
+	if err != nil {
1245
+		t.Fatal(err)
1246
+	}
1247
+	defer nuke(runtime)
1248
+
1249
+	srv := &Server{runtime: runtime, enableCors: true}
1250
+
1251
+	r := httptest.NewRecorder()
1252
+	router, err := createRouter(srv, false)
1253
+	if err != nil {
1254
+		t.Fatal(err)
1255
+	}
1256
+
1257
+	req, err := http.NewRequest("OPTIONS", "/", nil)
1258
+	if err != nil {
1259
+		t.Fatal(err)
1260
+	}
1261
+
1262
+	router.ServeHTTP(r, req)
1263
+	if r.Code != http.StatusOK {
1264
+		t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
1265
+	}
1266
+}
1267
+
1268
+func TestGetEnabledCors(t *testing.T) {
1269
+	runtime, err := newTestRuntime()
1270
+	if err != nil {
1271
+		t.Fatal(err)
1272
+	}
1273
+	defer nuke(runtime)
1274
+
1275
+	srv := &Server{runtime: runtime, enableCors: true}
1276
+
1277
+	r := httptest.NewRecorder()
1278
+
1279
+	router, err := createRouter(srv, false)
1280
+	if err != nil {
1281
+		t.Fatal(err)
1282
+	}
1283
+
1284
+	req, err := http.NewRequest("GET", "/version", nil)
1285
+	if err != nil {
1286
+		t.Fatal(err)
1287
+	}
1288
+
1289
+	router.ServeHTTP(r, req)
1290
+	if r.Code != http.StatusOK {
1291
+		t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
1292
+	}
1293
+
1294
+	allowOrigin := r.Header().Get("Access-Control-Allow-Origin")
1295
+	allowHeaders := r.Header().Get("Access-Control-Allow-Headers")
1296
+	allowMethods := r.Header().Get("Access-Control-Allow-Methods")
1297
+
1298
+	if allowOrigin != "*" {
1299
+		t.Errorf("Expected header Access-Control-Allow-Origin to be \"*\", %s found.", allowOrigin)
1300
+	}
1301
+	if allowHeaders != "Origin, X-Requested-With, Content-Type, Accept" {
1302
+		t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept\", %s found.", allowHeaders)
1303
+	}
1304
+	if allowMethods != "GET, POST, DELETE, PUT, OPTIONS" {
1305
+		t.Errorf("Expected hearder Access-Control-Allow-Methods to be \"GET, POST, DELETE, PUT, OPTIONS\", %s found.", allowMethods)
1306
+	}
1307
+}
1308
+
1242 1309
 func TestDeleteImages(t *testing.T) {
1243
-	//FIXME: Implement this test
1244
-	t.Log("Test not implemented")
1310
+	runtime, err := newTestRuntime()
1311
+	if err != nil {
1312
+		t.Fatal(err)
1313
+	}
1314
+	defer nuke(runtime)
1315
+
1316
+	srv := &Server{runtime: runtime}
1317
+
1318
+	if err := srv.runtime.repositories.Set("test", "test", unitTestImageName, true); err != nil {
1319
+		t.Fatal(err)
1320
+	}
1321
+
1322
+	images, err := srv.Images(false, "")
1323
+	if err != nil {
1324
+		t.Fatal(err)
1325
+	}
1326
+
1327
+	if len(images) != 2 {
1328
+		t.Errorf("Excepted 2 images, %d found", len(images))
1329
+	}
1330
+
1331
+	req, err := http.NewRequest("DELETE", "/images/test:test", nil)
1332
+	if err != nil {
1333
+		t.Fatal(err)
1334
+	}
1335
+
1336
+	r := httptest.NewRecorder()
1337
+	if err := deleteImages(srv, APIVERSION, r, req, map[string]string{"name": "test:test"}); err != nil {
1338
+		t.Fatal(err)
1339
+	}
1340
+	if r.Code != http.StatusOK {
1341
+		t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
1342
+	}
1343
+
1344
+	var outs []APIRmi
1345
+	if err := json.Unmarshal(r.Body.Bytes(), &outs); err != nil {
1346
+		t.Fatal(err)
1347
+	}
1348
+	if len(outs) != 1 {
1349
+		t.Fatalf("Expected %d event (untagged), got %d", 1, len(outs))
1350
+	}
1351
+	images, err = srv.Images(false, "")
1352
+	if err != nil {
1353
+		t.Fatal(err)
1354
+	}
1355
+
1356
+	if len(images) != 1 {
1357
+		t.Errorf("Excepted 1 image, %d found", len(images))
1358
+	}
1359
+
1360
+	/*	if c := runtime.Get(container.Id); c != nil {
1361
+			t.Fatalf("The container as not been deleted")
1362
+		}
1363
+
1364
+		if _, err := os.Stat(path.Join(container.rwPath(), "test")); err == nil {
1365
+			t.Fatalf("The test file has not been deleted")
1366
+		} */
1245 1367
 }
1246 1368
 
1247 1369
 // Mocked types for tests
... ...
@@ -16,12 +16,12 @@ import (
16 16
 const CONFIGFILE = ".dockercfg"
17 17
 
18 18
 // the registry server we want to login against
19
-const INDEX_SERVER = "https://index.docker.io/v1"
19
+const INDEXSERVER = "https://index.docker.io/v1"
20 20
 
21
-//const INDEX_SERVER = "http://indexstaging-docker.dotcloud.com/"
21
+//const INDEXSERVER = "http://indexstaging-docker.dotcloud.com/"
22 22
 
23 23
 var (
24
-	ErrConfigFileMissing error = errors.New("The Auth config file is missing")
24
+	ErrConfigFileMissing = errors.New("The Auth config file is missing")
25 25
 )
26 26
 
27 27
 type AuthConfig struct {
... ...
@@ -44,7 +44,7 @@ func IndexServerAddress() string {
44 44
 	if os.Getenv("DOCKER_INDEX_URL") != "" {
45 45
 		return os.Getenv("DOCKER_INDEX_URL") + "/v1"
46 46
 	}
47
-	return INDEX_SERVER
47
+	return INDEXSERVER
48 48
 }
49 49
 
50 50
 // create a base64 encoded auth string to store in config
... ...
@@ -40,7 +40,7 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
40 40
 	}
41 41
 
42 42
 	// Generate id
43
-	id := GenerateId()
43
+	id := GenerateID()
44 44
 	// Generate default hostname
45 45
 	// FIXME: the lxc template no longer needs to set a default hostname
46 46
 	if config.Hostname == "" {
... ...
@@ -49,17 +49,17 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
49 49
 
50 50
 	container := &Container{
51 51
 		// FIXME: we should generate the ID here instead of receiving it as an argument
52
-		Id:              id,
52
+		ID:              id,
53 53
 		Created:         time.Now(),
54 54
 		Path:            config.Cmd[0],
55 55
 		Args:            config.Cmd[1:], //FIXME: de-duplicate from config
56 56
 		Config:          config,
57
-		Image:           img.Id, // Always use the resolved image id
57
+		Image:           img.ID, // Always use the resolved image id
58 58
 		NetworkSettings: &NetworkSettings{},
59 59
 		// FIXME: do we need to store this in the container?
60 60
 		SysInitPath: sysInitPath,
61 61
 	}
62
-	container.root = builder.runtime.containerRoot(container.Id)
62
+	container.root = builder.runtime.containerRoot(container.ID)
63 63
 	// Step 1: create the container directory.
64 64
 	// This doubles as a barrier to avoid race conditions.
65 65
 	if err := os.Mkdir(container.root, 0700); err != nil {
... ...
@@ -110,7 +110,7 @@ func (builder *Builder) Commit(container *Container, repository, tag, comment, a
110 110
 	}
111 111
 	// Register the image if needed
112 112
 	if repository != "" {
113
-		if err := builder.repositories.Set(repository, tag, img.Id, true); err != nil {
113
+		if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil {
114 114
 			return img, err
115 115
 		}
116 116
 	}
... ...
@@ -63,11 +63,11 @@ func (b *builderClient) CmdFrom(name string) error {
63 63
 		return err
64 64
 	}
65 65
 
66
-	img := &ApiId{}
66
+	img := &APIID{}
67 67
 	if err := json.Unmarshal(obj, img); err != nil {
68 68
 		return err
69 69
 	}
70
-	b.image = img.Id
70
+	b.image = img.ID
71 71
 	utils.Debugf("Using image %s", b.image)
72 72
 	return nil
73 73
 }
... ...
@@ -91,19 +91,19 @@ func (b *builderClient) CmdRun(args string) error {
91 91
 	b.config.Cmd = nil
92 92
 	MergeConfig(b.config, config)
93 93
 
94
-	body, statusCode, err := b.cli.call("POST", "/images/getCache", &ApiImageConfig{Id: b.image, Config: b.config})
94
+	body, statusCode, err := b.cli.call("POST", "/images/getCache", &APIImageConfig{ID: b.image, Config: b.config})
95 95
 	if err != nil {
96 96
 		if statusCode != 404 {
97 97
 			return err
98 98
 		}
99 99
 	}
100 100
 	if statusCode != 404 {
101
-		apiId := &ApiId{}
102
-		if err := json.Unmarshal(body, apiId); err != nil {
101
+		apiID := &APIID{}
102
+		if err := json.Unmarshal(body, apiID); err != nil {
103 103
 			return err
104 104
 		}
105 105
 		utils.Debugf("Use cached version")
106
-		b.image = apiId.Id
106
+		b.image = apiID.ID
107 107
 		return nil
108 108
 	}
109 109
 	cid, err := b.run()
... ...
@@ -163,7 +163,7 @@ func (b *builderClient) CmdInsert(args string) error {
163 163
 	// 	return err
164 164
 	// }
165 165
 
166
-	// apiId := &ApiId{}
166
+	// apiId := &APIId{}
167 167
 	// if err := json.Unmarshal(body, apiId); err != nil {
168 168
 	// 	return err
169 169
 	// }
... ...
@@ -182,7 +182,7 @@ func (b *builderClient) run() (string, error) {
182 182
 		return "", err
183 183
 	}
184 184
 
185
-	apiRun := &ApiRun{}
185
+	apiRun := &APIRun{}
186 186
 	if err := json.Unmarshal(body, apiRun); err != nil {
187 187
 		return "", err
188 188
 	}
... ...
@@ -191,18 +191,18 @@ func (b *builderClient) run() (string, error) {
191 191
 	}
192 192
 
193 193
 	//start the container
194
-	_, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/start", nil)
194
+	_, _, err = b.cli.call("POST", "/containers/"+apiRun.ID+"/start", nil)
195 195
 	if err != nil {
196 196
 		return "", err
197 197
 	}
198
-	b.tmpContainers[apiRun.Id] = struct{}{}
198
+	b.tmpContainers[apiRun.ID] = struct{}{}
199 199
 
200 200
 	// Wait for it to finish
201
-	body, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/wait", nil)
201
+	body, _, err = b.cli.call("POST", "/containers/"+apiRun.ID+"/wait", nil)
202 202
 	if err != nil {
203 203
 		return "", err
204 204
 	}
205
-	apiWait := &ApiWait{}
205
+	apiWait := &APIWait{}
206 206
 	if err := json.Unmarshal(body, apiWait); err != nil {
207 207
 		return "", err
208 208
 	}
... ...
@@ -210,7 +210,7 @@ func (b *builderClient) run() (string, error) {
210 210
 		return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, apiWait.StatusCode)
211 211
 	}
212 212
 
213
-	return apiRun.Id, nil
213
+	return apiRun.ID, nil
214 214
 }
215 215
 
216 216
 func (b *builderClient) commit(id string) error {
... ...
@@ -222,11 +222,11 @@ func (b *builderClient) commit(id string) error {
222 222
 	if id == "" {
223 223
 		cmd := b.config.Cmd
224 224
 		b.config.Cmd = []string{"true"}
225
-		if cid, err := b.run(); err != nil {
225
+		cid, err := b.run()
226
+		if err != nil {
226 227
 			return err
227
-		} else {
228
-			id = cid
229 228
 		}
229
+		id = cid
230 230
 		b.config.Cmd = cmd
231 231
 	}
232 232
 
... ...
@@ -239,12 +239,12 @@ func (b *builderClient) commit(id string) error {
239 239
 	if err != nil {
240 240
 		return err
241 241
 	}
242
-	apiId := &ApiId{}
243
-	if err := json.Unmarshal(body, apiId); err != nil {
242
+	apiID := &APIID{}
243
+	if err := json.Unmarshal(body, apiID); err != nil {
244 244
 		return err
245 245
 	}
246
-	b.tmpImages[apiId.Id] = struct{}{}
247
-	b.image = apiId.Id
246
+	b.tmpImages[apiID.ID] = struct{}{}
247
+	b.image = apiID.ID
248 248
 	b.needCommit = false
249 249
 	return nil
250 250
 }
... ...
@@ -73,7 +73,7 @@ func (b *buildFile) CmdFrom(name string) error {
73 73
 			return err
74 74
 		}
75 75
 	}
76
-	b.image = image.Id
76
+	b.image = image.ID
77 77
 	b.config = &Config{}
78 78
 	return nil
79 79
 }
... ...
@@ -102,7 +102,7 @@ func (b *buildFile) CmdRun(args string) error {
102 102
 		return err
103 103
 	} else if cache != nil {
104 104
 		utils.Debugf("[BUILDER] Use cached version")
105
-		b.image = cache.Id
105
+		b.image = cache.ID
106 106
 		return nil
107 107
 	} else {
108 108
 		utils.Debugf("[BUILDER] Cache miss")
... ...
@@ -238,7 +238,7 @@ func (b *buildFile) run() (string, error) {
238 238
 	if err != nil {
239 239
 		return "", err
240 240
 	}
241
-	b.tmpContainers[c.Id] = struct{}{}
241
+	b.tmpContainers[c.ID] = struct{}{}
242 242
 
243 243
 	//start the container
244 244
 	if err := c.Start(); err != nil {
... ...
@@ -250,7 +250,7 @@ func (b *buildFile) run() (string, error) {
250 250
 		return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret)
251 251
 	}
252 252
 
253
-	return c.Id, nil
253
+	return c.ID, nil
254 254
 }
255 255
 
256 256
 // Commit the container <id> with the autorun command <autoCmd>
... ...
@@ -266,17 +266,17 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
266 266
 			return err
267 267
 		} else if cache != nil {
268 268
 			utils.Debugf("[BUILDER] Use cached version")
269
-			b.image = cache.Id
269
+			b.image = cache.ID
270 270
 			return nil
271 271
 		} else {
272 272
 			utils.Debugf("[BUILDER] Cache miss")
273 273
 		}
274 274
 
275
-		if cid, err := b.run(); err != nil {
275
+		cid, err := b.run()
276
+		if err != nil {
276 277
 			return err
277
-		} else {
278
-			id = cid
279 278
 		}
279
+		id = cid
280 280
 	}
281 281
 
282 282
 	container := b.runtime.Get(id)
... ...
@@ -292,8 +292,8 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
292 292
 	if err != nil {
293 293
 		return err
294 294
 	}
295
-	b.tmpImages[image.Id] = struct{}{}
296
-	b.image = image.Id
295
+	b.tmpImages[image.ID] = struct{}{}
296
+	b.image = image.ID
297 297
 	return nil
298 298
 }
299 299
 
... ...
@@ -313,10 +313,11 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
313 313
 	for {
314 314
 		line, err := file.ReadString('\n')
315 315
 		if err != nil {
316
-			if err == io.EOF {
316
+			if err == io.EOF && line == "" {
317 317
 				break
318
+			} else if err != io.EOF {
319
+				return "", err
318 320
 			}
319
-			return "", err
320 321
 		}
321 322
 		line = strings.Replace(strings.TrimSpace(line), "	", " ", 1)
322 323
 		// Skip comments and empty line
... ...
@@ -15,58 +15,69 @@ run    sh -c 'echo root:testpass > /tmp/passwd'
15 15
 run    mkdir -p /var/run/sshd
16 16
 `
17 17
 
18
+const DockerfileNoNewLine = `
19
+# VERSION		0.1
20
+# DOCKER-VERSION	0.2
21
+
22
+from   ` + unitTestImageName + `
23
+run    sh -c 'echo root:testpass > /tmp/passwd'
24
+run    mkdir -p /var/run/sshd`
25
+
18 26
 func TestBuild(t *testing.T) {
19
-	runtime, err := newTestRuntime()
20
-	if err != nil {
21
-		t.Fatal(err)
22
-	}
23
-	defer nuke(runtime)
27
+	dockerfiles := []string{Dockerfile, DockerfileNoNewLine}
28
+	for _, Dockerfile := range dockerfiles {
29
+		runtime, err := newTestRuntime()
30
+		if err != nil {
31
+			t.Fatal(err)
32
+		}
33
+		defer nuke(runtime)
24 34
 
25
-	srv := &Server{runtime: runtime}
35
+		srv := &Server{runtime: runtime}
26 36
 
27
-	buildfile := NewBuildFile(srv, &utils.NopWriter{})
37
+		buildfile := NewBuildFile(srv, &utils.NopWriter{})
28 38
 
29
-	imgId, err := buildfile.Build(strings.NewReader(Dockerfile), nil)
30
-	if err != nil {
31
-		t.Fatal(err)
32
-	}
39
+		imgID, err := buildfile.Build(strings.NewReader(Dockerfile), nil)
40
+		if err != nil {
41
+			t.Fatal(err)
42
+		}
33 43
 
34
-	builder := NewBuilder(runtime)
35
-	container, err := builder.Create(
36
-		&Config{
37
-			Image: imgId,
38
-			Cmd:   []string{"cat", "/tmp/passwd"},
39
-		},
40
-	)
41
-	if err != nil {
42
-		t.Fatal(err)
43
-	}
44
-	defer runtime.Destroy(container)
44
+		builder := NewBuilder(runtime)
45
+		container, err := builder.Create(
46
+			&Config{
47
+				Image: imgID,
48
+				Cmd:   []string{"cat", "/tmp/passwd"},
49
+			},
50
+		)
51
+		if err != nil {
52
+			t.Fatal(err)
53
+		}
54
+		defer runtime.Destroy(container)
45 55
 
46
-	output, err := container.Output()
47
-	if err != nil {
48
-		t.Fatal(err)
49
-	}
50
-	if string(output) != "root:testpass\n" {
51
-		t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, "root:testpass\n")
52
-	}
56
+		output, err := container.Output()
57
+		if err != nil {
58
+			t.Fatal(err)
59
+		}
60
+		if string(output) != "root:testpass\n" {
61
+			t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, "root:testpass\n")
62
+		}
53 63
 
54
-	container2, err := builder.Create(
55
-		&Config{
56
-			Image: imgId,
57
-			Cmd:   []string{"ls", "-d", "/var/run/sshd"},
58
-		},
59
-	)
60
-	if err != nil {
61
-		t.Fatal(err)
62
-	}
63
-	defer runtime.Destroy(container2)
64
+		container2, err := builder.Create(
65
+			&Config{
66
+				Image: imgID,
67
+				Cmd:   []string{"ls", "-d", "/var/run/sshd"},
68
+			},
69
+		)
70
+		if err != nil {
71
+			t.Fatal(err)
72
+		}
73
+		defer runtime.Destroy(container2)
64 74
 
65
-	output, err = container2.Output()
66
-	if err != nil {
67
-		t.Fatal(err)
68
-	}
69
-	if string(output) != "/var/run/sshd\n" {
70
-		t.Fatal("/var/run/sshd has not been created")
75
+		output, err = container2.Output()
76
+		if err != nil {
77
+			t.Fatal(err)
78
+		}
79
+		if string(output) != "/var/run/sshd\n" {
80
+			t.Fatal("/var/run/sshd has not been created")
81
+		}
71 82
 	}
72 83
 }
... ...
@@ -31,7 +31,7 @@ import (
31 31
 const VERSION = "0.4.0"
32 32
 
33 33
 var (
34
-	GIT_COMMIT string
34
+	GITCOMMIT string
35 35
 )
36 36
 
37 37
 func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
... ...
@@ -159,11 +159,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
159 159
 		file = os.Stdin
160 160
 	} else {
161 161
 		// Send Dockerfile from arg/Dockerfile (deprecate later)
162
-		if f, err := os.Open(path.Join(cmd.Arg(0), "Dockerfile")); err != nil {
162
+		f, err := os.Open(path.Join(cmd.Arg(0), "Dockerfile"))
163
+		if err != nil {
163 164
 			return err
164
-		} else {
165
-			file = f
166 165
 		}
166
+		file = f
167 167
 		// Send context from arg
168 168
 		// Create a FormFile multipart for the context if needed
169 169
 		// FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage?
... ...
@@ -176,21 +176,21 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
176 176
 		if err != nil {
177 177
 			return err
178 178
 		}
179
-		if wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension()); err != nil {
179
+		wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension())
180
+		if err != nil {
180 181
 			return err
181
-		} else {
182
-			// FIXME: Find a way to have a progressbar for the upload too
183
-			sf := utils.NewStreamFormatter(false)
184
-			io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, sf.FormatProgress("Caching Context", "%v/%v (%v)"), sf))
185 182
 		}
183
+		// FIXME: Find a way to have a progressbar for the upload too
184
+		sf := utils.NewStreamFormatter(false)
185
+		io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, sf.FormatProgress("Caching Context", "%v/%v (%v)"), sf))
186 186
 		multipartBody = io.MultiReader(multipartBody, boundary)
187 187
 	}
188 188
 	// Create a FormFile multipart for the Dockerfile
189
-	if wField, err := w.CreateFormFile("Dockerfile", "Dockerfile"); err != nil {
189
+	wField, err := w.CreateFormFile("Dockerfile", "Dockerfile")
190
+	if err != nil {
190 191
 		return err
191
-	} else {
192
-		io.Copy(wField, file)
193 192
 	}
193
+	io.Copy(wField, file)
194 194
 	multipartBody = io.MultiReader(multipartBody, boundary)
195 195
 
196 196
 	v := &url.Values{}
... ...
@@ -218,7 +218,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
218 218
 		if err != nil {
219 219
 			return err
220 220
 		}
221
-		return fmt.Errorf("error: %s", body)
221
+		if len(body) == 0 {
222
+			return fmt.Errorf("Error: %s", http.StatusText(resp.StatusCode))
223
+		}
224
+		return fmt.Errorf("Error: %s", body)
222 225
 	}
223 226
 
224 227
 	// Output the result
... ...
@@ -276,9 +279,8 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
276 276
 	oldState, err := term.SetRawTerminal()
277 277
 	if err != nil {
278 278
 		return err
279
-	} else {
280
-		defer term.RestoreTerminal(oldState)
281 279
 	}
280
+	defer term.RestoreTerminal(oldState)
282 281
 
283 282
 	cmd := Subcmd("login", "", "Register or Login to the docker registry server")
284 283
 	if err := cmd.Parse(args); err != nil {
... ...
@@ -331,7 +333,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
331 331
 		return err
332 332
 	}
333 333
 
334
-	var out2 ApiAuth
334
+	var out2 APIAuth
335 335
 	err = json.Unmarshal(body, &out2)
336 336
 	if err != nil {
337 337
 		return err
... ...
@@ -358,7 +360,7 @@ func (cli *DockerCli) CmdWait(args ...string) error {
358 358
 		if err != nil {
359 359
 			fmt.Printf("%s", err)
360 360
 		} else {
361
-			var out ApiWait
361
+			var out APIWait
362 362
 			err = json.Unmarshal(body, &out)
363 363
 			if err != nil {
364 364
 				return err
... ...
@@ -386,7 +388,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
386 386
 		return err
387 387
 	}
388 388
 
389
-	var out ApiVersion
389
+	var out APIVersion
390 390
 	err = json.Unmarshal(body, &out)
391 391
 	if err != nil {
392 392
 		utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
... ...
@@ -419,7 +421,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
419 419
 		return err
420 420
 	}
421 421
 
422
-	var out ApiInfo
422
+	var out APIInfo
423 423
 	if err := json.Unmarshal(body, &out); err != nil {
424 424
 		return err
425 425
 	}
... ...
@@ -458,7 +460,7 @@ func (cli *DockerCli) CmdStop(args ...string) error {
458 458
 	for _, name := range cmd.Args() {
459 459
 		_, _, err := cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil)
460 460
 		if err != nil {
461
-			fmt.Printf("%s", err)
461
+			fmt.Fprintf(os.Stderr, "%s", err)
462 462
 		} else {
463 463
 			fmt.Println(name)
464 464
 		}
... ...
@@ -483,7 +485,7 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
483 483
 	for _, name := range cmd.Args() {
484 484
 		_, _, err := cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil)
485 485
 		if err != nil {
486
-			fmt.Printf("%s", err)
486
+			fmt.Fprintf(os.Stderr, "%s", err)
487 487
 		} else {
488 488
 			fmt.Println(name)
489 489
 		}
... ...
@@ -504,7 +506,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
504 504
 	for _, name := range args {
505 505
 		_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
506 506
 		if err != nil {
507
-			fmt.Printf("%s", err)
507
+			fmt.Fprintf(os.Stderr, "%s", err)
508 508
 		} else {
509 509
 			fmt.Println(name)
510 510
 		}
... ...
@@ -513,29 +515,38 @@ func (cli *DockerCli) CmdStart(args ...string) error {
513 513
 }
514 514
 
515 515
 func (cli *DockerCli) CmdInspect(args ...string) error {
516
-	cmd := Subcmd("inspect", "CONTAINER|IMAGE", "Return low-level information on a container/image")
516
+	cmd := Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container/image")
517 517
 	if err := cmd.Parse(args); err != nil {
518 518
 		return nil
519 519
 	}
520
-	if cmd.NArg() != 1 {
520
+	if cmd.NArg() < 1 {
521 521
 		cmd.Usage()
522 522
 		return nil
523 523
 	}
524
-	obj, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
525
-	if err != nil {
526
-		obj, _, err = cli.call("GET", "/images/"+cmd.Arg(0)+"/json", nil)
524
+	fmt.Printf("[")
525
+	for i, name := range args {
526
+		if i > 0 {
527
+			fmt.Printf(",")
528
+		}
529
+		obj, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
527 530
 		if err != nil {
528
-			return err
531
+			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
532
+			if err != nil {
533
+				fmt.Fprintf(os.Stderr, "%s", err)
534
+				continue
535
+			}
529 536
 		}
530
-	}
531 537
 
532
-	indented := new(bytes.Buffer)
533
-	if err = json.Indent(indented, obj, "", "    "); err != nil {
534
-		return err
535
-	}
536
-	if _, err := io.Copy(os.Stdout, indented); err != nil {
537
-		return err
538
+		indented := new(bytes.Buffer)
539
+		if err = json.Indent(indented, obj, "", "    "); err != nil {
540
+			fmt.Fprintf(os.Stderr, "%s", err)
541
+			continue
542
+		}
543
+		if _, err := io.Copy(os.Stdout, indented); err != nil {
544
+			fmt.Fprintf(os.Stderr, "%s", err)
545
+		}
538 546
 	}
547
+	fmt.Printf("]")
539 548
 	return nil
540 549
 }
541 550
 
... ...
@@ -562,7 +573,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
562 562
 	if frontend, exists := out.NetworkSettings.PortMapping[cmd.Arg(1)]; exists {
563 563
 		fmt.Println(frontend)
564 564
 	} else {
565
-		return fmt.Errorf("error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0))
565
+		return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0))
566 566
 	}
567 567
 	return nil
568 568
 }
... ...
@@ -579,11 +590,22 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
579 579
 	}
580 580
 
581 581
 	for _, name := range cmd.Args() {
582
-		_, _, err := cli.call("DELETE", "/images/"+name, nil)
582
+		body, _, err := cli.call("DELETE", "/images/"+name, nil)
583 583
 		if err != nil {
584
-			fmt.Printf("%s", err)
584
+			fmt.Fprintf(os.Stderr, "%s", err)
585 585
 		} else {
586
-			fmt.Println(name)
586
+			var outs []APIRmi
587
+			err = json.Unmarshal(body, &outs)
588
+			if err != nil {
589
+				return err
590
+			}
591
+			for _, out := range outs {
592
+				if out.Deleted != "" {
593
+					fmt.Println("Deleted:", out.Deleted)
594
+				} else {
595
+					fmt.Println("Untagged:", out.Untagged)
596
+				}
597
+			}
587 598
 		}
588 599
 	}
589 600
 	return nil
... ...
@@ -604,7 +626,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
604 604
 		return err
605 605
 	}
606 606
 
607
-	var outs []ApiHistory
607
+	var outs []APIHistory
608 608
 	err = json.Unmarshal(body, &outs)
609 609
 	if err != nil {
610 610
 		return err
... ...
@@ -613,7 +635,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
613 613
 	fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
614 614
 
615 615
 	for _, out := range outs {
616
-		fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.Id, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy)
616
+		fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy)
617 617
 	}
618 618
 	w.Flush()
619 619
 	return nil
... ...
@@ -786,7 +808,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
786 786
 			return err
787 787
 		}
788 788
 
789
-		var outs []ApiImages
789
+		var outs []APIImages
790 790
 		err = json.Unmarshal(body, &outs)
791 791
 		if err != nil {
792 792
 			return err
... ...
@@ -808,16 +830,16 @@ func (cli *DockerCli) CmdImages(args ...string) error {
808 808
 			if !*quiet {
809 809
 				fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag)
810 810
 				if *noTrunc {
811
-					fmt.Fprintf(w, "%s\t", out.Id)
811
+					fmt.Fprintf(w, "%s\t", out.ID)
812 812
 				} else {
813
-					fmt.Fprintf(w, "%s\t", utils.TruncateId(out.Id))
813
+					fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID))
814 814
 				}
815 815
 				fmt.Fprintf(w, "%s ago\n", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))))
816 816
 			} else {
817 817
 				if *noTrunc {
818
-					fmt.Fprintln(w, out.Id)
818
+					fmt.Fprintln(w, out.ID)
819 819
 				} else {
820
-					fmt.Fprintln(w, utils.TruncateId(out.Id))
820
+					fmt.Fprintln(w, utils.TruncateID(out.ID))
821 821
 				}
822 822
 			}
823 823
 		}
... ...
@@ -864,7 +886,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
864 864
 		return err
865 865
 	}
866 866
 
867
-	var outs []ApiContainers
867
+	var outs []APIContainers
868 868
 	err = json.Unmarshal(body, &outs)
869 869
 	if err != nil {
870 870
 		return err
... ...
@@ -877,15 +899,15 @@ func (cli *DockerCli) CmdPs(args ...string) error {
877 877
 	for _, out := range outs {
878 878
 		if !*quiet {
879 879
 			if *noTrunc {
880
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", out.Id, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
880
+				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
881 881
 			} else {
882
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", utils.TruncateId(out.Id), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
882
+				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
883 883
 			}
884 884
 		} else {
885 885
 			if *noTrunc {
886
-				fmt.Fprintln(w, out.Id)
886
+				fmt.Fprintln(w, out.ID)
887 887
 			} else {
888
-				fmt.Fprintln(w, utils.TruncateId(out.Id))
888
+				fmt.Fprintln(w, utils.TruncateID(out.ID))
889 889
 			}
890 890
 		}
891 891
 	}
... ...
@@ -928,13 +950,13 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
928 928
 		return err
929 929
 	}
930 930
 
931
-	apiId := &ApiId{}
932
-	err = json.Unmarshal(body, apiId)
931
+	apiID := &APIID{}
932
+	err = json.Unmarshal(body, apiID)
933 933
 	if err != nil {
934 934
 		return err
935 935
 	}
936 936
 
937
-	fmt.Println(apiId.Id)
937
+	fmt.Println(apiID.ID)
938 938
 	return nil
939 939
 }
940 940
 
... ...
@@ -1071,7 +1093,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
1071 1071
 		return err
1072 1072
 	}
1073 1073
 
1074
-	outs := []ApiSearch{}
1074
+	outs := []APISearch{}
1075 1075
 	err = json.Unmarshal(body, &outs)
1076 1076
 	if err != nil {
1077 1077
 		return err
... ...
@@ -1080,7 +1102,12 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
1080 1080
 	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
1081 1081
 	fmt.Fprintf(w, "NAME\tDESCRIPTION\n")
1082 1082
 	for _, out := range outs {
1083
-		fmt.Fprintf(w, "%s\t%s\n", out.Name, out.Description)
1083
+		desc := strings.Replace(out.Description, "\n", " ", -1)
1084
+		desc = strings.Replace(desc, "\r", " ", -1)
1085
+		if len(desc) > 45 {
1086
+			desc = utils.Trunc(desc, 42) + "..."
1087
+		}
1088
+		fmt.Fprintf(w, "%s\t%s\n", out.Name, desc)
1084 1089
 	}
1085 1090
 	w.Flush()
1086 1091
 	return nil
... ...
@@ -1203,7 +1230,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1203 1203
 		return err
1204 1204
 	}
1205 1205
 
1206
-	out := &ApiRun{}
1206
+	out := &APIRun{}
1207 1207
 	err = json.Unmarshal(body, out)
1208 1208
 	if err != nil {
1209 1209
 		return err
... ...
@@ -1224,18 +1251,21 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1224 1224
 	}
1225 1225
 
1226 1226
 	//start the container
1227
-	_, _, err = cli.call("POST", "/containers/"+out.Id+"/start", nil)
1227
+	_, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil)
1228 1228
 	if err != nil {
1229 1229
 		return err
1230 1230
 	}
1231 1231
 
1232
+	if !config.AttachStdout && !config.AttachStderr {
1233
+		fmt.Println(out.ID)
1234
+	}
1232 1235
 	if connections > 0 {
1233 1236
 		chErrors := make(chan error, connections)
1234
-		cli.monitorTtySize(out.Id)
1237
+		cli.monitorTtySize(out.ID)
1235 1238
 
1236 1239
 		if splitStderr && config.AttachStderr {
1237 1240
 			go func() {
1238
-				chErrors <- cli.hijack("POST", "/containers/"+out.Id+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr)
1241
+				chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr)
1239 1242
 			}()
1240 1243
 		}
1241 1244
 
... ...
@@ -1253,7 +1283,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1253 1253
 			v.Set("stderr", "1")
1254 1254
 		}
1255 1255
 		go func() {
1256
-			chErrors <- cli.hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout)
1256
+			chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout)
1257 1257
 		}()
1258 1258
 		for connections > 0 {
1259 1259
 			err := <-chErrors
... ...
@@ -1263,9 +1293,6 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1263 1263
 			connections -= 1
1264 1264
 		}
1265 1265
 	}
1266
-	if !config.AttachStdout && !config.AttachStderr {
1267
-		fmt.Println(out.Id)
1268
-	}
1269 1266
 	return nil
1270 1267
 }
1271 1268
 
... ...
@@ -1313,7 +1340,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
1313 1313
 		params = bytes.NewBuffer(buf)
1314 1314
 	}
1315 1315
 
1316
-	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), params)
1316
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), params)
1317 1317
 	if err != nil {
1318 1318
 		return nil, -1, err
1319 1319
 	}
... ...
@@ -1336,7 +1363,10 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
1336 1336
 		return nil, -1, err
1337 1337
 	}
1338 1338
 	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
1339
-		return nil, resp.StatusCode, fmt.Errorf("error: %s", body)
1339
+		if len(body) == 0 {
1340
+			return nil, resp.StatusCode, fmt.Errorf("Error: %s", http.StatusText(resp.StatusCode))
1341
+		}
1342
+		return nil, resp.StatusCode, fmt.Errorf("Error: %s", body)
1340 1343
 	}
1341 1344
 	return body, resp.StatusCode, nil
1342 1345
 }
... ...
@@ -1345,7 +1375,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1345 1345
 	if (method == "POST" || method == "PUT") && in == nil {
1346 1346
 		in = bytes.NewReader([]byte{})
1347 1347
 	}
1348
-	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), in)
1348
+	req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), in)
1349 1349
 	if err != nil {
1350 1350
 		return err
1351 1351
 	}
... ...
@@ -1366,20 +1396,23 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1366 1366
 		if err != nil {
1367 1367
 			return err
1368 1368
 		}
1369
-		return fmt.Errorf("error: %s", body)
1369
+		if len(body) == 0 {
1370
+			return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode))
1371
+		}
1372
+		return fmt.Errorf("Error: %s", body)
1370 1373
 	}
1371 1374
 
1372 1375
 	if resp.Header.Get("Content-Type") == "application/json" {
1373 1376
 		dec := json.NewDecoder(resp.Body)
1374 1377
 		for {
1375
-			var m utils.JsonMessage
1378
+			var m utils.JSONMessage
1376 1379
 			if err := dec.Decode(&m); err == io.EOF {
1377 1380
 				break
1378 1381
 			} else if err != nil {
1379 1382
 				return err
1380 1383
 			}
1381 1384
 			if m.Progress != "" {
1382
-				fmt.Fprintf(out, "Downloading %s\r", m.Progress)
1385
+				fmt.Fprintf(out, "%s %s\r", m.Status, m.Progress)
1383 1386
 			} else if m.Error != "" {
1384 1387
 				return fmt.Errorf(m.Error)
1385 1388
 			} else {
... ...
@@ -1395,7 +1428,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1395 1395
 }
1396 1396
 
1397 1397
 func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.File, out io.Writer) error {
1398
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", API_VERSION, path), nil)
1398
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
1399 1399
 	if err != nil {
1400 1400
 		return err
1401 1401
 	}
... ...
@@ -1417,11 +1450,11 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
1417 1417
 	})
1418 1418
 
1419 1419
 	if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" {
1420
-		if oldState, err := term.SetRawTerminal(); err != nil {
1420
+		oldState, err := term.SetRawTerminal()
1421
+		if err != nil {
1421 1422
 			return err
1422
-		} else {
1423
-			defer term.RestoreTerminal(oldState)
1424 1423
 		}
1424
+		defer term.RestoreTerminal(oldState)
1425 1425
 	}
1426 1426
 	sendStdin := utils.Go(func() error {
1427 1427
 		_, err := io.Copy(rwc, in)
... ...
@@ -1435,7 +1468,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
1435 1435
 		return err
1436 1436
 	}
1437 1437
 
1438
-	if !term.IsTerminal(os.Stdin.Fd()) {
1438
+	if !term.IsTerminal(in.Fd()) {
1439 1439
 		if err := <-sendStdin; err != nil {
1440 1440
 			return err
1441 1441
 		}
... ...
@@ -23,7 +23,7 @@ import (
23 23
 type Container struct {
24 24
 	root string
25 25
 
26
-	Id string
26
+	ID string
27 27
 
28 28
 	Created time.Time
29 29
 
... ...
@@ -167,8 +167,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet
167 167
 }
168 168
 
169 169
 type NetworkSettings struct {
170
-	IpAddress   string
171
-	IpPrefixLen int
170
+	IPAddress   string
171
+	IPPrefixLen int
172 172
 	Gateway     string
173 173
 	Bridge      string
174 174
 	PortMapping map[string]string
... ...
@@ -355,6 +355,18 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
355 355
 				errors <- err
356 356
 			}()
357 357
 		}
358
+	} else {
359
+		go func() {
360
+			if stdinCloser != nil {
361
+				defer stdinCloser.Close()
362
+			}
363
+
364
+			if cStdout, err := container.StdoutPipe(); err != nil {
365
+				utils.Debugf("Error stdout pipe")
366
+			} else {
367
+				io.Copy(&utils.NopWriter{}, cStdout)
368
+			}
369
+		}()
358 370
 	}
359 371
 	if stderr != nil {
360 372
 		nJobs += 1
... ...
@@ -381,7 +393,20 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
381 381
 				errors <- err
382 382
 			}()
383 383
 		}
384
+	} else {
385
+		go func() {
386
+			if stdinCloser != nil {
387
+				defer stdinCloser.Close()
388
+			}
389
+
390
+			if cStderr, err := container.StderrPipe(); err != nil {
391
+				utils.Debugf("Error stdout pipe")
392
+			} else {
393
+				io.Copy(&utils.NopWriter{}, cStderr)
394
+			}
395
+		}()
384 396
 	}
397
+
385 398
 	return utils.Go(func() error {
386 399
 		if cStdout != nil {
387 400
 			defer cStdout.Close()
... ...
@@ -409,7 +434,7 @@ func (container *Container) Start() error {
409 409
 	defer container.State.unlock()
410 410
 
411 411
 	if container.State.Running {
412
-		return fmt.Errorf("The container %s is already running.", container.Id)
412
+		return fmt.Errorf("The container %s is already running.", container.ID)
413 413
 	}
414 414
 	if err := container.EnsureMounted(); err != nil {
415 415
 		return err
... ...
@@ -431,24 +456,24 @@ func (container *Container) Start() error {
431 431
 
432 432
 	// Create the requested volumes volumes
433 433
 	for volPath := range container.Config.Volumes {
434
-		if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil {
434
+		c, err := container.runtime.volumes.Create(nil, container, "", "", nil)
435
+		if err != nil {
435 436
 			return err
436
-		} else {
437
-			if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
438
-				return nil
439
-			}
440
-			container.Volumes[volPath] = c.Id
441 437
 		}
438
+		if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
439
+			return nil
440
+		}
441
+		container.Volumes[volPath] = c.ID
442 442
 	}
443 443
 
444 444
 	if container.Config.VolumesFrom != "" {
445 445
 		c := container.runtime.Get(container.Config.VolumesFrom)
446 446
 		if c == nil {
447
-			return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.Id)
447
+			return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
448 448
 		}
449 449
 		for volPath, id := range c.Volumes {
450 450
 			if _, exists := container.Volumes[volPath]; exists {
451
-				return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.Id)
451
+				return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.ID)
452 452
 			}
453 453
 			if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
454 454
 				return nil
... ...
@@ -462,7 +487,7 @@ func (container *Container) Start() error {
462 462
 	}
463 463
 
464 464
 	params := []string{
465
-		"-n", container.Id,
465
+		"-n", container.ID,
466 466
 		"-f", container.lxcConfigPath(),
467 467
 		"--",
468 468
 		"/sbin/init",
... ...
@@ -573,17 +598,17 @@ func (container *Container) allocateNetwork() error {
573 573
 	}
574 574
 	container.NetworkSettings.PortMapping = make(map[string]string)
575 575
 	for _, spec := range container.Config.PortSpecs {
576
-		if nat, err := iface.AllocatePort(spec); err != nil {
576
+		nat, err := iface.AllocatePort(spec)
577
+		if err != nil {
577 578
 			iface.Release()
578 579
 			return err
579
-		} else {
580
-			container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
581 580
 		}
581
+		container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
582 582
 	}
583 583
 	container.network = iface
584 584
 	container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
585
-	container.NetworkSettings.IpAddress = iface.IPNet.IP.String()
586
-	container.NetworkSettings.IpPrefixLen, _ = iface.IPNet.Mask.Size()
585
+	container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
586
+	container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
587 587
 	container.NetworkSettings.Gateway = iface.Gateway.String()
588 588
 	return nil
589 589
 }
... ...
@@ -597,16 +622,16 @@ func (container *Container) releaseNetwork() {
597 597
 // FIXME: replace this with a control socket within docker-init
598 598
 func (container *Container) waitLxc() error {
599 599
 	for {
600
-		if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
600
+		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
601
+		if err != nil {
601 602
 			return err
602
-		} else {
603
-			if !strings.Contains(string(output), "RUNNING") {
604
-				return nil
605
-			}
603
+		}
604
+		if !strings.Contains(string(output), "RUNNING") {
605
+			return nil
606 606
 		}
607 607
 		time.Sleep(500 * time.Millisecond)
608 608
 	}
609
-	return nil
609
+	panic("Unreachable")
610 610
 }
611 611
 
612 612
 func (container *Container) monitor() {
... ...
@@ -616,17 +641,17 @@ func (container *Container) monitor() {
616 616
 	// If the command does not exists, try to wait via lxc
617 617
 	if container.cmd == nil {
618 618
 		if err := container.waitLxc(); err != nil {
619
-			utils.Debugf("%s: Process: %s", container.Id, err)
619
+			utils.Debugf("%s: Process: %s", container.ID, err)
620 620
 		}
621 621
 	} else {
622 622
 		if err := container.cmd.Wait(); err != nil {
623 623
 			// Discard the error as any signals or non 0 returns will generate an error
624
-			utils.Debugf("%s: Process: %s", container.Id, err)
624
+			utils.Debugf("%s: Process: %s", container.ID, err)
625 625
 		}
626 626
 	}
627 627
 	utils.Debugf("Process finished")
628 628
 
629
-	var exitCode int = -1
629
+	exitCode := -1
630 630
 	if container.cmd != nil {
631 631
 		exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
632 632
 	}
... ...
@@ -635,24 +660,24 @@ func (container *Container) monitor() {
635 635
 	container.releaseNetwork()
636 636
 	if container.Config.OpenStdin {
637 637
 		if err := container.stdin.Close(); err != nil {
638
-			utils.Debugf("%s: Error close stdin: %s", container.Id, err)
638
+			utils.Debugf("%s: Error close stdin: %s", container.ID, err)
639 639
 		}
640 640
 	}
641 641
 	if err := container.stdout.CloseWriters(); err != nil {
642
-		utils.Debugf("%s: Error close stdout: %s", container.Id, err)
642
+		utils.Debugf("%s: Error close stdout: %s", container.ID, err)
643 643
 	}
644 644
 	if err := container.stderr.CloseWriters(); err != nil {
645
-		utils.Debugf("%s: Error close stderr: %s", container.Id, err)
645
+		utils.Debugf("%s: Error close stderr: %s", container.ID, err)
646 646
 	}
647 647
 
648 648
 	if container.ptyMaster != nil {
649 649
 		if err := container.ptyMaster.Close(); err != nil {
650
-			utils.Debugf("%s: Error closing Pty master: %s", container.Id, err)
650
+			utils.Debugf("%s: Error closing Pty master: %s", container.ID, err)
651 651
 		}
652 652
 	}
653 653
 
654 654
 	if err := container.Unmount(); err != nil {
655
-		log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
655
+		log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
656 656
 	}
657 657
 
658 658
 	// Re-create a brand new stdin pipe once the container exited
... ...
@@ -673,7 +698,7 @@ func (container *Container) monitor() {
673 673
 		// This is because State.setStopped() has already been called, and has caused Wait()
674 674
 		// to return.
675 675
 		// FIXME: why are we serializing running state to disk in the first place?
676
-		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.Id, err)
676
+		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
677 677
 	}
678 678
 }
679 679
 
... ...
@@ -683,17 +708,17 @@ func (container *Container) kill() error {
683 683
 	}
684 684
 
685 685
 	// Sending SIGKILL to the process via lxc
686
-	output, err := exec.Command("lxc-kill", "-n", container.Id, "9").CombinedOutput()
686
+	output, err := exec.Command("lxc-kill", "-n", container.ID, "9").CombinedOutput()
687 687
 	if err != nil {
688
-		log.Printf("error killing container %s (%s, %s)", container.Id, output, err)
688
+		log.Printf("error killing container %s (%s, %s)", container.ID, output, err)
689 689
 	}
690 690
 
691 691
 	// 2. Wait for the process to die, in last resort, try to kill the process directly
692 692
 	if err := container.WaitTimeout(10 * time.Second); err != nil {
693 693
 		if container.cmd == nil {
694
-			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.Id)
694
+			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ID)
695 695
 		}
696
-		log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.Id)
696
+		log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.ID)
697 697
 		if err := container.cmd.Process.Kill(); err != nil {
698 698
 			return err
699 699
 		}
... ...
@@ -721,7 +746,7 @@ func (container *Container) Stop(seconds int) error {
721 721
 	}
722 722
 
723 723
 	// 1. Send a SIGTERM
724
-	if output, err := exec.Command("lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
724
+	if output, err := exec.Command("lxc-kill", "-n", container.ID, "15").CombinedOutput(); err != nil {
725 725
 		log.Print(string(output))
726 726
 		log.Print("Failed to send SIGTERM to the process, force killing")
727 727
 		if err := container.kill(); err != nil {
... ...
@@ -731,7 +756,7 @@ func (container *Container) Stop(seconds int) error {
731 731
 
732 732
 	// 2. Wait for the process to exit on its own
733 733
 	if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
734
-		log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.Id, seconds)
734
+		log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
735 735
 		if err := container.kill(); err != nil {
736 736
 			return err
737 737
 		}
... ...
@@ -795,7 +820,8 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
795 795
 	case <-done:
796 796
 		return nil
797 797
 	}
798
-	panic("unreachable")
798
+
799
+	panic("Unreachable")
799 800
 }
800 801
 
801 802
 func (container *Container) EnsureMounted() error {
... ...
@@ -838,16 +864,16 @@ func (container *Container) Unmount() error {
838 838
 	return Unmount(container.RootfsPath())
839 839
 }
840 840
 
841
-// ShortId returns a shorthand version of the container's id for convenience.
841
+// ShortID returns a shorthand version of the container's id for convenience.
842 842
 // A collision with other container shorthands is very unlikely, but possible.
843 843
 // In case of a collision a lookup with Runtime.Get() will fail, and the caller
844 844
 // will need to use a langer prefix, or the full-length container Id.
845
-func (container *Container) ShortId() string {
846
-	return utils.TruncateId(container.Id)
845
+func (container *Container) ShortID() string {
846
+	return utils.TruncateID(container.ID)
847 847
 }
848 848
 
849 849
 func (container *Container) logPath(name string) string {
850
-	return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name))
850
+	return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
851 851
 }
852 852
 
853 853
 func (container *Container) ReadLog(name string) (io.Reader, error) {
... ...
@@ -887,7 +913,7 @@ func (container *Container) rwPath() string {
887 887
 	return path.Join(container.root, "rw")
888 888
 }
889 889
 
890
-func validateId(id string) error {
890
+func validateID(id string) error {
891 891
 	if id == "" {
892 892
 		return fmt.Errorf("Invalid empty id")
893 893
 	}
... ...
@@ -14,7 +14,7 @@ import (
14 14
 	"time"
15 15
 )
16 16
 
17
-func TestIdFormat(t *testing.T) {
17
+func TestIDFormat(t *testing.T) {
18 18
 	runtime, err := newTestRuntime()
19 19
 	if err != nil {
20 20
 		t.Fatal(err)
... ...
@@ -22,19 +22,19 @@ func TestIdFormat(t *testing.T) {
22 22
 	defer nuke(runtime)
23 23
 	container1, err := NewBuilder(runtime).Create(
24 24
 		&Config{
25
-			Image: GetTestImage(runtime).Id,
25
+			Image: GetTestImage(runtime).ID,
26 26
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
27 27
 		},
28 28
 	)
29 29
 	if err != nil {
30 30
 		t.Fatal(err)
31 31
 	}
32
-	match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.Id))
32
+	match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.ID))
33 33
 	if err != nil {
34 34
 		t.Fatal(err)
35 35
 	}
36 36
 	if !match {
37
-		t.Fatalf("Invalid container ID: %s", container1.Id)
37
+		t.Fatalf("Invalid container ID: %s", container1.ID)
38 38
 	}
39 39
 }
40 40
 
... ...
@@ -46,7 +46,7 @@ func TestMultipleAttachRestart(t *testing.T) {
46 46
 	defer nuke(runtime)
47 47
 	container, err := NewBuilder(runtime).Create(
48 48
 		&Config{
49
-			Image: GetTestImage(runtime).Id,
49
+			Image: GetTestImage(runtime).ID,
50 50
 			Cmd: []string{"/bin/sh", "-c",
51 51
 				"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`;  echo hello; done"},
52 52
 		},
... ...
@@ -153,7 +153,7 @@ func TestDiff(t *testing.T) {
153 153
 	// Create a container and remove a file
154 154
 	container1, err := builder.Create(
155 155
 		&Config{
156
-			Image: GetTestImage(runtime).Id,
156
+			Image: GetTestImage(runtime).ID,
157 157
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
158 158
 		},
159 159
 	)
... ...
@@ -194,7 +194,7 @@ func TestDiff(t *testing.T) {
194 194
 	// Create a new container from the commited image
195 195
 	container2, err := builder.Create(
196 196
 		&Config{
197
-			Image: img.Id,
197
+			Image: img.ID,
198 198
 			Cmd:   []string{"cat", "/etc/passwd"},
199 199
 		},
200 200
 	)
... ...
@@ -221,7 +221,7 @@ func TestDiff(t *testing.T) {
221 221
 	// Create a new containere
222 222
 	container3, err := builder.Create(
223 223
 		&Config{
224
-			Image: GetTestImage(runtime).Id,
224
+			Image: GetTestImage(runtime).ID,
225 225
 			Cmd:   []string{"rm", "/bin/httpd"},
226 226
 		},
227 227
 	)
... ...
@@ -260,7 +260,7 @@ func TestCommitAutoRun(t *testing.T) {
260 260
 	builder := NewBuilder(runtime)
261 261
 	container1, err := builder.Create(
262 262
 		&Config{
263
-			Image: GetTestImage(runtime).Id,
263
+			Image: GetTestImage(runtime).ID,
264 264
 			Cmd:   []string{"/bin/sh", "-c", "echo hello > /world"},
265 265
 		},
266 266
 	)
... ...
@@ -291,7 +291,7 @@ func TestCommitAutoRun(t *testing.T) {
291 291
 	// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
292 292
 	container2, err := builder.Create(
293 293
 		&Config{
294
-			Image: img.Id,
294
+			Image: img.ID,
295 295
 		},
296 296
 	)
297 297
 	if err != nil {
... ...
@@ -340,7 +340,7 @@ func TestCommitRun(t *testing.T) {
340 340
 
341 341
 	container1, err := builder.Create(
342 342
 		&Config{
343
-			Image: GetTestImage(runtime).Id,
343
+			Image: GetTestImage(runtime).ID,
344 344
 			Cmd:   []string{"/bin/sh", "-c", "echo hello > /world"},
345 345
 		},
346 346
 	)
... ...
@@ -372,7 +372,7 @@ func TestCommitRun(t *testing.T) {
372 372
 
373 373
 	container2, err := builder.Create(
374 374
 		&Config{
375
-			Image: img.Id,
375
+			Image: img.ID,
376 376
 			Cmd:   []string{"cat", "/world"},
377 377
 		},
378 378
 	)
... ...
@@ -419,7 +419,7 @@ func TestStart(t *testing.T) {
419 419
 	defer nuke(runtime)
420 420
 	container, err := NewBuilder(runtime).Create(
421 421
 		&Config{
422
-			Image:     GetTestImage(runtime).Id,
422
+			Image:     GetTestImage(runtime).ID,
423 423
 			Memory:    33554432,
424 424
 			CpuShares: 1000,
425 425
 			Cmd:       []string{"/bin/cat"},
... ...
@@ -463,7 +463,7 @@ func TestRun(t *testing.T) {
463 463
 	defer nuke(runtime)
464 464
 	container, err := NewBuilder(runtime).Create(
465 465
 		&Config{
466
-			Image: GetTestImage(runtime).Id,
466
+			Image: GetTestImage(runtime).ID,
467 467
 			Cmd:   []string{"ls", "-al"},
468 468
 		},
469 469
 	)
... ...
@@ -491,7 +491,7 @@ func TestOutput(t *testing.T) {
491 491
 	defer nuke(runtime)
492 492
 	container, err := NewBuilder(runtime).Create(
493 493
 		&Config{
494
-			Image: GetTestImage(runtime).Id,
494
+			Image: GetTestImage(runtime).ID,
495 495
 			Cmd:   []string{"echo", "-n", "foobar"},
496 496
 		},
497 497
 	)
... ...
@@ -515,7 +515,7 @@ func TestKillDifferentUser(t *testing.T) {
515 515
 	}
516 516
 	defer nuke(runtime)
517 517
 	container, err := NewBuilder(runtime).Create(&Config{
518
-		Image: GetTestImage(runtime).Id,
518
+		Image: GetTestImage(runtime).ID,
519 519
 		Cmd:   []string{"tail", "-f", "/etc/resolv.conf"},
520 520
 		User:  "daemon",
521 521
 	},
... ...
@@ -563,7 +563,7 @@ func TestKill(t *testing.T) {
563 563
 	}
564 564
 	defer nuke(runtime)
565 565
 	container, err := NewBuilder(runtime).Create(&Config{
566
-		Image: GetTestImage(runtime).Id,
566
+		Image: GetTestImage(runtime).ID,
567 567
 		Cmd:   []string{"cat", "/dev/zero"},
568 568
 	},
569 569
 	)
... ...
@@ -611,7 +611,7 @@ func TestExitCode(t *testing.T) {
611 611
 	builder := NewBuilder(runtime)
612 612
 
613 613
 	trueContainer, err := builder.Create(&Config{
614
-		Image: GetTestImage(runtime).Id,
614
+		Image: GetTestImage(runtime).ID,
615 615
 		Cmd:   []string{"/bin/true", ""},
616 616
 	})
617 617
 	if err != nil {
... ...
@@ -626,7 +626,7 @@ func TestExitCode(t *testing.T) {
626 626
 	}
627 627
 
628 628
 	falseContainer, err := builder.Create(&Config{
629
-		Image: GetTestImage(runtime).Id,
629
+		Image: GetTestImage(runtime).ID,
630 630
 		Cmd:   []string{"/bin/false", ""},
631 631
 	})
632 632
 	if err != nil {
... ...
@@ -648,7 +648,7 @@ func TestRestart(t *testing.T) {
648 648
 	}
649 649
 	defer nuke(runtime)
650 650
 	container, err := NewBuilder(runtime).Create(&Config{
651
-		Image: GetTestImage(runtime).Id,
651
+		Image: GetTestImage(runtime).ID,
652 652
 		Cmd:   []string{"echo", "-n", "foobar"},
653 653
 	},
654 654
 	)
... ...
@@ -681,7 +681,7 @@ func TestRestartStdin(t *testing.T) {
681 681
 	}
682 682
 	defer nuke(runtime)
683 683
 	container, err := NewBuilder(runtime).Create(&Config{
684
-		Image: GetTestImage(runtime).Id,
684
+		Image: GetTestImage(runtime).ID,
685 685
 		Cmd:   []string{"cat"},
686 686
 
687 687
 		OpenStdin: true,
... ...
@@ -763,7 +763,7 @@ func TestUser(t *testing.T) {
763 763
 
764 764
 	// Default user must be root
765 765
 	container, err := builder.Create(&Config{
766
-		Image: GetTestImage(runtime).Id,
766
+		Image: GetTestImage(runtime).ID,
767 767
 		Cmd:   []string{"id"},
768 768
 	},
769 769
 	)
... ...
@@ -781,7 +781,7 @@ func TestUser(t *testing.T) {
781 781
 
782 782
 	// Set a username
783 783
 	container, err = builder.Create(&Config{
784
-		Image: GetTestImage(runtime).Id,
784
+		Image: GetTestImage(runtime).ID,
785 785
 		Cmd:   []string{"id"},
786 786
 
787 787
 		User: "root",
... ...
@@ -801,7 +801,7 @@ func TestUser(t *testing.T) {
801 801
 
802 802
 	// Set a UID
803 803
 	container, err = builder.Create(&Config{
804
-		Image: GetTestImage(runtime).Id,
804
+		Image: GetTestImage(runtime).ID,
805 805
 		Cmd:   []string{"id"},
806 806
 
807 807
 		User: "0",
... ...
@@ -821,7 +821,7 @@ func TestUser(t *testing.T) {
821 821
 
822 822
 	// Set a different user by uid
823 823
 	container, err = builder.Create(&Config{
824
-		Image: GetTestImage(runtime).Id,
824
+		Image: GetTestImage(runtime).ID,
825 825
 		Cmd:   []string{"id"},
826 826
 
827 827
 		User: "1",
... ...
@@ -843,7 +843,7 @@ func TestUser(t *testing.T) {
843 843
 
844 844
 	// Set a different user by username
845 845
 	container, err = builder.Create(&Config{
846
-		Image: GetTestImage(runtime).Id,
846
+		Image: GetTestImage(runtime).ID,
847 847
 		Cmd:   []string{"id"},
848 848
 
849 849
 		User: "daemon",
... ...
@@ -872,7 +872,7 @@ func TestMultipleContainers(t *testing.T) {
872 872
 	builder := NewBuilder(runtime)
873 873
 
874 874
 	container1, err := builder.Create(&Config{
875
-		Image: GetTestImage(runtime).Id,
875
+		Image: GetTestImage(runtime).ID,
876 876
 		Cmd:   []string{"cat", "/dev/zero"},
877 877
 	},
878 878
 	)
... ...
@@ -882,7 +882,7 @@ func TestMultipleContainers(t *testing.T) {
882 882
 	defer runtime.Destroy(container1)
883 883
 
884 884
 	container2, err := builder.Create(&Config{
885
-		Image: GetTestImage(runtime).Id,
885
+		Image: GetTestImage(runtime).ID,
886 886
 		Cmd:   []string{"cat", "/dev/zero"},
887 887
 	},
888 888
 	)
... ...
@@ -928,7 +928,7 @@ func TestStdin(t *testing.T) {
928 928
 	}
929 929
 	defer nuke(runtime)
930 930
 	container, err := NewBuilder(runtime).Create(&Config{
931
-		Image: GetTestImage(runtime).Id,
931
+		Image: GetTestImage(runtime).ID,
932 932
 		Cmd:   []string{"cat"},
933 933
 
934 934
 		OpenStdin: true,
... ...
@@ -975,7 +975,7 @@ func TestTty(t *testing.T) {
975 975
 	}
976 976
 	defer nuke(runtime)
977 977
 	container, err := NewBuilder(runtime).Create(&Config{
978
-		Image: GetTestImage(runtime).Id,
978
+		Image: GetTestImage(runtime).ID,
979 979
 		Cmd:   []string{"cat"},
980 980
 
981 981
 		OpenStdin: true,
... ...
@@ -1022,7 +1022,7 @@ func TestEnv(t *testing.T) {
1022 1022
 	}
1023 1023
 	defer nuke(runtime)
1024 1024
 	container, err := NewBuilder(runtime).Create(&Config{
1025
-		Image: GetTestImage(runtime).Id,
1025
+		Image: GetTestImage(runtime).ID,
1026 1026
 		Cmd:   []string{"/usr/bin/env"},
1027 1027
 	},
1028 1028
 	)
... ...
@@ -1100,7 +1100,7 @@ func TestLXCConfig(t *testing.T) {
1100 1100
 	cpuMax := 10000
1101 1101
 	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
1102 1102
 	container, err := NewBuilder(runtime).Create(&Config{
1103
-		Image: GetTestImage(runtime).Id,
1103
+		Image: GetTestImage(runtime).ID,
1104 1104
 		Cmd:   []string{"/bin/true"},
1105 1105
 
1106 1106
 		Hostname:  "foobar",
... ...
@@ -1128,7 +1128,7 @@ func BenchmarkRunSequencial(b *testing.B) {
1128 1128
 	defer nuke(runtime)
1129 1129
 	for i := 0; i < b.N; i++ {
1130 1130
 		container, err := NewBuilder(runtime).Create(&Config{
1131
-			Image: GetTestImage(runtime).Id,
1131
+			Image: GetTestImage(runtime).ID,
1132 1132
 			Cmd:   []string{"echo", "-n", "foo"},
1133 1133
 		},
1134 1134
 		)
... ...
@@ -1163,7 +1163,7 @@ func BenchmarkRunParallel(b *testing.B) {
1163 1163
 		tasks = append(tasks, complete)
1164 1164
 		go func(i int, complete chan error) {
1165 1165
 			container, err := NewBuilder(runtime).Create(&Config{
1166
-				Image: GetTestImage(runtime).Id,
1166
+				Image: GetTestImage(runtime).ID,
1167 1167
 				Cmd:   []string{"echo", "-n", "foo"},
1168 1168
 			},
1169 1169
 			)
... ...
@@ -11,13 +11,13 @@ import (
11 11
 	"time"
12 12
 )
13 13
 
14
-var DOCKER_PATH string = path.Join(os.Getenv("DOCKERPATH"), "docker")
14
+var DOCKERPATH = path.Join(os.Getenv("DOCKERPATH"), "docker")
15 15
 
16 16
 // WARNING: this crashTest will 1) crash your host, 2) remove all containers
17 17
 func runDaemon() (*exec.Cmd, error) {
18 18
 	os.Remove("/var/run/docker.pid")
19 19
 	exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
20
-	cmd := exec.Command(DOCKER_PATH, "-d")
20
+	cmd := exec.Command(DOCKERPATH, "-d")
21 21
 	outPipe, err := cmd.StdoutPipe()
22 22
 	if err != nil {
23 23
 		return nil, err
... ...
@@ -77,7 +77,7 @@ func crashTest() error {
77 77
 			stop = false
78 78
 			for i := 0; i < 100 && !stop; {
79 79
 				func() error {
80
-					cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
80
+					cmd := exec.Command(DOCKERPATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
81 81
 					i++
82 82
 					totalTestCount++
83 83
 					outPipe, err := cmd.StdoutPipe()
84 84
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-Solomon Hykes <solomon@dotcloud.com>
2 1
deleted file mode 100644
... ...
@@ -1,68 +0,0 @@
1
-# docker-build: build your software with docker
2
-
3
-## Description
4
-
5
-docker-build is a script to build docker images from source. It will be deprecated once the 'build' feature is incorporated into docker itself (See https://github.com/dotcloud/docker/issues/278)
6
-
7
-Author: Solomon Hykes <solomon@dotcloud.com>
8
-
9
-
10
-## Install
11
-
12
-docker-builder requires:
13
-
14
-1) A reasonably recent Python setup (tested on 2.7.2).
15
-
16
-2) A running docker daemon at version 0.1.4 or more recent (http://www.docker.io/gettingstarted)
17
-
18
-
19
-## Usage
20
-
21
-First create a valid Changefile, which defines a sequence of changes to apply to a base image.
22
-
23
-    $ cat Changefile
24
-    # Start build from a know base image
25
-    from	base:ubuntu-12.10
26
-    # Update ubuntu sources
27
-    run	echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
28
-    run	apt-get update
29
-    # Install system packages
30
-    run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
31
-    run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
32
-    run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
33
-    # Insert files from the host (./myscript must be present in the current directory)
34
-    copy	myscript /usr/local/bin/myscript
35
-
36
-
37
-Run docker-build, and pass the contents of your Changefile as standard input.
38
-
39
-    $ IMG=$(./docker-build < Changefile)
40
-
41
-This will take a while: for each line of the changefile, docker-build will:
42
-
43
-1. Create a new container to execute the given command or insert the given file
44
-2. Wait for the container to complete execution
45
-3. Commit the resulting changes as a new image
46
-4. Use the resulting image as the input of the next step
47
-
48
-
49
-If all the steps succeed, the result will be an image containing the combined results of each build step.
50
-You can trace back those build steps by inspecting the image's history:
51
-
52
-    $ docker history $IMG
53
-    ID                  CREATED             CREATED BY
54
-    1e9e2045de86        A few seconds ago   /bin/sh -c cat > /usr/local/bin/myscript; chmod +x /usr/local/bin/git
55
-    77db140aa62a        A few seconds ago   /bin/sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
56
-    77db140aa62a        A few seconds ago   /bin/sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
57
-    77db140aa62a        A few seconds ago   /bin/sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -q git 
58
-    83e85d155451        A few seconds ago   /bin/sh -c apt-get update
59
-    bfd53b36d9d3        A few seconds ago   /bin/sh -c echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
60
-    base		2 weeks ago         /bin/bash
61
-    27cf78414709        2 weeks ago
62
-
63
-
64
-Note that your build started from 'base', as instructed by your Changefile. But that base image itself seems to have been built in 2 steps - hence the extra step in the history.
65
-
66
-
67
-You can use this build technique to create any image you want: a database, a web application, or anything else that can be build by a sequence of unix commands - in other words, anything else.
68
-
69 1
deleted file mode 100755
... ...
@@ -1,142 +0,0 @@
1
-#!/usr/bin/env python
2
-
3
-# docker-build is a script to build docker images from source.
4
-# It will be deprecated once the 'build' feature is incorporated into docker itself.
5
-# (See https://github.com/dotcloud/docker/issues/278)
6
-#
7
-# Author: Solomon Hykes <solomon@dotcloud.com>
8
-
9
-
10
-
11
-# First create a valid Changefile, which defines a sequence of changes to apply to a base image.
12
-# 
13
-#     $ cat Changefile
14
-#     # Start build from a know base image
15
-#     from	base:ubuntu-12.10
16
-#     # Update ubuntu sources
17
-#     run	echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
18
-#     run	apt-get update
19
-#     # Install system packages
20
-#     run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
21
-#     run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
22
-#     run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
23
-#     # Insert files from the host (./myscript must be present in the current directory)
24
-#     copy	myscript /usr/local/bin/myscript
25
-# 
26
-# 
27
-# Run docker-build, and pass the contents of your Changefile as standard input.
28
-# 
29
-#     $ IMG=$(./docker-build < Changefile)
30
-# 
31
-# This will take a while: for each line of the changefile, docker-build will:
32
-# 
33
-# 1. Create a new container to execute the given command or insert the given file
34
-# 2. Wait for the container to complete execution
35
-# 3. Commit the resulting changes as a new image
36
-# 4. Use the resulting image as the input of the next step
37
-
38
-
39
-import sys
40
-import subprocess
41
-import json
42
-import hashlib
43
-
44
-def docker(args, stdin=None):
45
-	print "# docker " + " ".join(args)
46
-	p = subprocess.Popen(["docker"] + list(args), stdin=stdin, stdout=subprocess.PIPE)
47
-	return p.stdout
48
-
49
-def image_exists(img):
50
-	return docker(["inspect", img]).read().strip() != ""
51
-
52
-def image_config(img):
53
-	return json.loads(docker(["inspect", img]).read()).get("config", {})
54
-
55
-def run_and_commit(img_in, cmd, stdin=None, author=None, run=None):
56
-	run_id = docker(["run"] + (["-i", "-a", "stdin"] if stdin else ["-d"]) + [img_in, "/bin/sh", "-c", cmd], stdin=stdin).read().rstrip()
57
-	print "---> Waiting for " + run_id
58
-	result=int(docker(["wait", run_id]).read().rstrip())
59
-	if result != 0:
60
-		print "!!! '{}' return non-zero exit code '{}'. Aborting.".format(cmd, result)
61
-		sys.exit(1)
62
-	return docker(["commit"] + (["-author", author] if author else []) + (["-run", json.dumps(run)] if run is not None else []) + [run_id]).read().rstrip()
63
-
64
-def insert(base, src, dst, author=None):
65
-	print "COPY {} to {} in {}".format(src, dst, base)
66
-	if dst == "":
67
-		raise Exception("Missing destination path")
68
-	stdin = file(src)
69
-	stdin.seek(0)
70
-	return run_and_commit(base, "cat > {0}; chmod +x {0}".format(dst), stdin=stdin, author=author)
71
-
72
-def add(base, src, dst, author=None):
73
-	print "PUSH to {} in {}".format(dst, base)
74
-	if src == ".":
75
-		tar = subprocess.Popen(["tar", "-c", "."], stdout=subprocess.PIPE).stdout
76
-	else:
77
-		tar = subprocess.Popen(["curl", src], stdout=subprocess.PIPE).stdout
78
-	if dst == "":
79
-		raise Exception("Missing argument to push")
80
-	return run_and_commit(base, "mkdir -p '{0}' && tar -C '{0}' -x".format(dst), stdin=tar, author=author)
81
-
82
-def main():
83
-	base=""
84
-	maintainer=""
85
-	steps = []
86
-	try:
87
-		for line in sys.stdin.readlines():
88
-			line = line.strip()
89
-			# Skip comments and empty lines
90
-			if line == "" or line[0] == "#":
91
-				continue
92
-			op, param = line.split(None, 1)
93
-			print op.upper() + " " + param
94
-			if op == "from":
95
-				base = param
96
-				steps.append(base)
97
-			elif op == "maintainer":
98
-				maintainer = param
99
-			elif op == "run":
100
-				result = run_and_commit(base, param, author=maintainer)
101
-				steps.append(result)
102
-				base = result
103
-				print "===> " + base
104
-			elif op == "copy":
105
-				src, dst = param.split("	", 1)
106
-				result = insert(base, src, dst, author=maintainer)
107
-				steps.append(result)
108
-				base = result
109
-				print "===> " + base
110
-			elif op == "add":
111
-				src, dst = param.split("	", 1)
112
-				result = add(base, src, dst, author=maintainer)
113
-				steps.append(result)
114
-				base=result
115
-				print "===> " + base
116
-			elif op == "expose":
117
-				config = image_config(base)
118
-				if config.get("PortSpecs") is None:
119
-					config["PortSpecs"] = []
120
-				portspec = param.strip()
121
-				config["PortSpecs"].append(portspec)
122
-				result = run_and_commit(base, "# (nop) expose port {}".format(portspec), author=maintainer, run=config)
123
-				steps.append(result)
124
-				base=result
125
-				print "===> " + base
126
-			elif op == "cmd":
127
-				config  = image_config(base)
128
-				cmd = list(json.loads(param))
129
-				config["Cmd"] = cmd
130
-				result = run_and_commit(base, "# (nop) set default command to '{}'".format(" ".join(cmd)), author=maintainer, run=config)
131
-				steps.append(result)
132
-				base=result
133
-				print "===> " + base
134
-			else:
135
-				print "Skipping uknown op " + op
136
-	except:
137
-		docker(["rmi"] + steps[1:])
138
-		raise
139
-	print base
140
-
141
-if __name__ == "__main__":
142
-	main()
143 1
deleted file mode 100644
... ...
@@ -1,13 +0,0 @@
1
-# Start build from a know base image
2
-maintainer	Solomon Hykes <solomon@dotcloud.com>
3
-from	base:ubuntu-12.10
4
-# Update ubuntu sources
5
-run	echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
6
-run	apt-get update
7
-# Install system packages
8
-run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
9
-run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
10
-run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
11
-# Insert files from the host (./myscript must be present in the current directory)
12
-copy	myscript	/usr/local/bin/myscript
13
-push	/src
14 1
deleted file mode 100644
... ...
@@ -1,3 +0,0 @@
1
-#!/bin/sh
2
-
3
-echo hello, world!
... ...
@@ -15,7 +15,7 @@ import (
15 15
 )
16 16
 
17 17
 var (
18
-	GIT_COMMIT string
18
+	GITCOMMIT string
19 19
 )
20 20
 
21 21
 func main() {
... ...
@@ -33,6 +33,7 @@ func main() {
33 33
 	bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
34 34
 	pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
35 35
 	flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to")
36
+	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
36 37
 	flag.Parse()
37 38
 	if *bridgeName != "" {
38 39
 		docker.NetworkBridgeIface = *bridgeName
... ...
@@ -59,13 +60,13 @@ func main() {
59 59
 	if *flDebug {
60 60
 		os.Setenv("DEBUG", "1")
61 61
 	}
62
-	docker.GIT_COMMIT = GIT_COMMIT
62
+	docker.GITCOMMIT = GITCOMMIT
63 63
 	if *flDaemon {
64 64
 		if flag.NArg() != 0 {
65 65
 			flag.Usage()
66 66
 			return
67 67
 		}
68
-		if err := daemon(*pidfile, host, port, *flAutoRestart); err != nil {
68
+		if err := daemon(*pidfile, host, port, *flAutoRestart, *flEnableCors); err != nil {
69 69
 			log.Fatal(err)
70 70
 			os.Exit(-1)
71 71
 		}
... ...
@@ -104,7 +105,7 @@ func removePidFile(pidfile string) {
104 104
 	}
105 105
 }
106 106
 
107
-func daemon(pidfile, addr string, port int, autoRestart bool) error {
107
+func daemon(pidfile, addr string, port int, autoRestart, enableCors bool) error {
108 108
 	if addr != "127.0.0.1" {
109 109
 		log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
110 110
 	}
... ...
@@ -122,7 +123,7 @@ func daemon(pidfile, addr string, port int, autoRestart bool) error {
122 122
 		os.Exit(0)
123 123
 	}()
124 124
 
125
-	server, err := docker.NewServer(autoRestart)
125
+	server, err := docker.NewServer(autoRestart, enableCors)
126 126
 	if err != nil {
127 127
 		return err
128 128
 	}
... ...
@@ -46,12 +46,11 @@ clean:
46 46
 	-rm -rf $(BUILDDIR)/*
47 47
 
48 48
 docs:
49
-	#-rm -rf $(BUILDDIR)/*
50 49
 	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/html
51 50
 	@echo
52 51
 	@echo "Build finished. The documentation pages are now in $(BUILDDIR)/html."
53 52
 
54
-server:
53
+server: docs
55 54
 	@cd $(BUILDDIR)/html; $(PYTHON) -m SimpleHTTPServer 8000
56 55
 
57 56
 site:
... ...
@@ -62,12 +61,13 @@ site:
62 62
 
63 63
 connect:
64 64
 	@echo connecting dotcloud to www.docker.io website, make sure to use user 1
65
-	@cd _build/website/ ; \
66
-	dotcloud connect dockerwebsite ;
65
+	@echo or create your own "dockerwebsite" app
66
+	@cd $(BUILDDIR)/website/ ; \
67
+	dotcloud connect dockerwebsite ; \
67 68
 	dotcloud list
68 69
 
69 70
 push:
70
-	@cd _build/website/ ; \
71
+	@cd $(BUILDDIR)/website/ ; \
71 72
 	dotcloud push
72 73
 
73 74
 $(VERSIONS):
74 75
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+This directory holds the authoritative specifications of APIs defined and implemented by Docker. Currently this includes:
1
+
2
+* The remote API by which a docker node can be queried over HTTP
3
+* The registry API by which a docker node can download and upload container images for storage and sharing
4
+* The index search API by which a docker node can search the public index for images to download
... ...
@@ -564,7 +564,7 @@ Create an image
564 564
 	   Content-Type: application/json
565 565
 
566 566
 	   {"status":"Pulling..."}
567
-	   {"progress":"1/? (n/a)"}
567
+	   {"status":"Pulling", "progress":"1/? (n/a)"}
568 568
 	   {"error":"Invalid..."}
569 569
 	   ...
570 570
 
... ...
@@ -607,7 +607,7 @@ Insert a file in a image
607 607
 	   Content-Type: application/json
608 608
 
609 609
 	   {"status":"Inserting..."}
610
-	   {"progress":"1/? (n/a)"}
610
+	   {"status":"Inserting", "progress":"1/? (n/a)"}
611 611
 	   {"error":"Invalid..."}
612 612
 	   ...
613 613
 
... ...
@@ -734,7 +734,7 @@ Push an image on the registry
734 734
 	   Content-Type: application/json
735 735
 
736 736
 	   {"status":"Pushing..."}
737
-	   {"progress":"1/? (n/a)"}
737
+	   {"status":"Pushing", "progress":"1/? (n/a)"}
738 738
 	   {"error":"Invalid..."}
739 739
 	   ...
740 740
 
... ...
@@ -777,6 +777,7 @@ Tag an image into a repository
777 777
 	:statuscode 200: no error
778 778
 	:statuscode 400: bad parameter
779 779
 	:statuscode 404: no such image
780
+	:statuscode 409: conflict
780 781
         :statuscode 500: server error
781 782
 
782 783
 
... ...
@@ -793,14 +794,30 @@ Remove an image
793 793
 
794 794
 	   DELETE /images/test HTTP/1.1
795 795
 
796
-	**Example response**:
796
+	**Example response v1.0**:
797 797
 
798 798
         .. sourcecode:: http
799 799
 
800 800
            HTTP/1.1 204 OK
801 801
 
802
+	**Example response v1.1**:
803
+
804
+        .. sourcecode:: http
805
+
806
+           HTTP/1.1 200 OK
807
+	   Content-type: application/json
808
+
809
+	   [
810
+	    {"Untagged":"3e2f21a89f"},
811
+	    {"Deleted":"3e2f21a89f"},
812
+	    {"Deleted":"53b4f83ac9"}
813
+	   ]
814
+
815
+	:query force: 1/True/true or 0/False/false, default false
816
+	:statuscode 200: no error
802 817
 	:statuscode 204: no error
803 818
         :statuscode 404: no such image
819
+	:statuscode 409: conflict
804 820
         :statuscode 500: server error
805 821
 
806 822
 
... ...
@@ -839,9 +856,9 @@ Search images
839 839
 		}
840 840
 	   ]
841 841
 
842
-	   :query term: term to search
843
-	   :statuscode 200: no error
844
-	   :statuscode 500: server error
842
+	:query term: term to search
843
+	:statuscode 200: no error
844
+	:statuscode 500: server error
845 845
 
846 846
 
847 847
 3.3 Misc
... ...
@@ -1056,3 +1073,36 @@ Here are the steps of 'docker run' :
1056 1056
 
1057 1057
 In this first version of the API, some of the endpoints, like /attach, /pull or /push uses hijacking to transport stdin,
1058 1058
 stdout and stderr on the same socket. This might change in the future.
1059
+
1060
+3.3 CORS Requests
1061
+-----------------
1062
+
1063
+To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode.
1064
+    
1065
+    docker -d -H="192.168.1.9:4243" -api-enable-cors
1066
+
1067
+
1068
+==================================
1069
+Docker Remote API Client Libraries
1070
+==================================
1071
+
1072
+These libraries have been not tested by the Docker Maintainers for
1073
+compatibility. Please file issues with the library owners.  If you
1074
+find more library implementations, please list them in Docker doc bugs
1075
+and we will add the libraries here.
1076
+
1077
++----------------------+----------------+--------------------------------------------+
1078
+| Language/Framework   | Name           | Repository                                 |
1079
++======================+================+============================================+
1080
+| Python               | docker-py      | https://github.com/dotcloud/docker-py      |
1081
++----------------------+----------------+--------------------------------------------+
1082
+| Ruby                 | docker-ruby    | https://github.com/ActiveState/docker-ruby |
1083
++----------------------+----------------+--------------------------------------------+
1084
+| Ruby                 | docker-client  | https://github.com/geku/docker-client      |
1085
++----------------------+----------------+--------------------------------------------+
1086
+| Javascript           | docker-js      | https://github.com/dgoujard/docker-js      |
1087
++----------------------+----------------+--------------------------------------------+
1088
+| Javascript (Angular) | dockerui       | https://github.com/crosbymichael/dockerui  |
1089
+| **WebUI**            |                |                                            |
1090
++----------------------+----------------+--------------------------------------------+
1091
+
... ...
@@ -5,13 +5,14 @@
5 5
 APIs
6 6
 ====
7 7
 
8
-This following :
8
+Your programs and scripts can access Docker's functionality via these interfaces:
9 9
 
10 10
 .. toctree::
11 11
   :maxdepth: 3
12 12
 
13
+  registry_index_spec
13 14
   registry_api
14
-  index_search_api
15
+  index_api
15 16
   docker_remote_api
16 17
 
17 18
 
18 19
new file mode 100644
... ...
@@ -0,0 +1,553 @@
0
+:title: Index API
1
+:description: API Documentation for Docker Index
2
+:keywords: API, Docker, index, REST, documentation
3
+
4
+=================
5
+Docker Index API
6
+=================
7
+
8
+.. contents:: Table of Contents
9
+
10
+1. Brief introduction
11
+=====================
12
+
13
+- This is the REST API for the Docker index
14
+- Authorization is done with basic auth over SSL
15
+- Not all commands require authentication, only those noted as such.
16
+
17
+2. Endpoints
18
+============
19
+
20
+2.1 Repository
21
+^^^^^^^^^^^^^^
22
+
23
+Repositories
24
+*************
25
+
26
+User Repo
27
+~~~~~~~~~
28
+
29
+.. http:put:: /v1/repositories/(namespace)/(repo_name)/
30
+
31
+    Create a user repository with the given ``namespace`` and ``repo_name``.
32
+
33
+    **Example Request**:
34
+
35
+    .. sourcecode:: http
36
+
37
+        PUT /v1/repositories/foo/bar/ HTTP/1.1
38
+        Host: index.docker.io
39
+        Accept: application/json
40
+        Content-Type: application/json
41
+        Authorization: Basic akmklmasadalkm==
42
+        X-Docker-Token: true
43
+
44
+        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
45
+
46
+    :parameter namespace: the namespace for the repo
47
+    :parameter repo_name: the name for the repo
48
+
49
+    **Example Response**:
50
+
51
+    .. sourcecode:: http
52
+
53
+        HTTP/1.1 200
54
+        Vary: Accept
55
+        Content-Type: application/json
56
+        WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=write
57
+        X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
58
+
59
+        ""
60
+
61
+    :statuscode 200: Created
62
+    :statuscode 400: Errors (invalid json, missing or invalid fields, etc)
63
+    :statuscode 401: Unauthorized
64
+    :statuscode 403: Account is not Active
65
+
66
+
67
+.. http:delete:: /v1/repositories/(namespace)/(repo_name)/
68
+
69
+    Delete a user repository with the given ``namespace`` and ``repo_name``.
70
+
71
+    **Example Request**:
72
+
73
+    .. sourcecode:: http
74
+
75
+        DELETE /v1/repositories/foo/bar/ HTTP/1.1
76
+        Host: index.docker.io
77
+        Accept: application/json
78
+        Content-Type: application/json
79
+        Authorization: Basic akmklmasadalkm==
80
+        X-Docker-Token: true
81
+
82
+        ""
83
+
84
+    :parameter namespace: the namespace for the repo
85
+    :parameter repo_name: the name for the repo
86
+
87
+    **Example Response**:
88
+
89
+    .. sourcecode:: http
90
+
91
+        HTTP/1.1 202
92
+        Vary: Accept
93
+        Content-Type: application/json
94
+        WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=delete
95
+        X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
96
+
97
+        ""
98
+
99
+    :statuscode 200: Deleted
100
+    :statuscode 202: Accepted
101
+    :statuscode 400: Errors (invalid json, missing or invalid fields, etc)
102
+    :statuscode 401: Unauthorized
103
+    :statuscode 403: Account is not Active
104
+
105
+Library Repo
106
+~~~~~~~~~~~~
107
+
108
+.. http:put:: /v1/repositories/(repo_name)/
109
+
110
+    Create a library repository with the given ``repo_name``.
111
+    This is a restricted feature only available to docker admins.
112
+    
113
+    When namespace is missing, it is assumed to be ``library``
114
+
115
+    **Example Request**:
116
+
117
+    .. sourcecode:: http
118
+
119
+        PUT /v1/repositories/foobar/ HTTP/1.1
120
+        Host: index.docker.io
121
+        Accept: application/json
122
+        Content-Type: application/json
123
+        Authorization: Basic akmklmasadalkm==
124
+        X-Docker-Token: true
125
+
126
+        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
127
+
128
+    :parameter repo_name:  the library name for the repo
129
+
130
+    **Example Response**:
131
+
132
+    .. sourcecode:: http
133
+
134
+        HTTP/1.1 200
135
+        Vary: Accept
136
+        Content-Type: application/json
137
+        WWW-Authenticate: Token signature=123abc,repository=”library/foobar”,access=write
138
+        X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
139
+
140
+        ""
141
+
142
+    :statuscode 200: Created
143
+    :statuscode 400: Errors (invalid json, missing or invalid fields, etc)
144
+    :statuscode 401: Unauthorized
145
+    :statuscode 403: Account is not Active
146
+
147
+.. http:delete:: /v1/repositories/(repo_name)/
148
+
149
+    Delete a library repository with the given ``repo_name``.
150
+    This is a restricted feature only available to docker admins.
151
+    
152
+    When namespace is missing, it is assumed to be ``library``
153
+
154
+    **Example Request**:
155
+
156
+    .. sourcecode:: http
157
+
158
+        DELETE /v1/repositories/foobar/ HTTP/1.1
159
+        Host: index.docker.io
160
+        Accept: application/json
161
+        Content-Type: application/json
162
+        Authorization: Basic akmklmasadalkm==
163
+        X-Docker-Token: true
164
+
165
+        ""
166
+
167
+    :parameter repo_name:  the library name for the repo
168
+
169
+    **Example Response**:
170
+
171
+    .. sourcecode:: http
172
+
173
+        HTTP/1.1 202
174
+        Vary: Accept
175
+        Content-Type: application/json
176
+        WWW-Authenticate: Token signature=123abc,repository=”library/foobar”,access=delete
177
+        X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
178
+
179
+        ""
180
+
181
+    :statuscode 200: Deleted
182
+    :statuscode 202: Accepted
183
+    :statuscode 400: Errors (invalid json, missing or invalid fields, etc)
184
+    :statuscode 401: Unauthorized
185
+    :statuscode 403: Account is not Active
186
+
187
+Repository Images
188
+*****************
189
+
190
+User Repo Images
191
+~~~~~~~~~~~~~~~~
192
+
193
+.. http:put:: /v1/repositories/(namespace)/(repo_name)/images
194
+
195
+    Update the images for a user repo.
196
+
197
+    **Example Request**:
198
+
199
+    .. sourcecode:: http
200
+
201
+        PUT /v1/repositories/foo/bar/images HTTP/1.1
202
+        Host: index.docker.io
203
+        Accept: application/json
204
+        Content-Type: application/json
205
+        Authorization: Basic akmklmasadalkm==
206
+
207
+        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
208
+        “checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
209
+
210
+    :parameter namespace: the namespace for the repo
211
+    :parameter repo_name: the name for the repo
212
+
213
+    **Example Response**:
214
+
215
+    .. sourcecode:: http
216
+
217
+        HTTP/1.1 204
218
+        Vary: Accept
219
+        Content-Type: application/json
220
+
221
+        ""
222
+
223
+    :statuscode 204: Created
224
+    :statuscode 400: Errors (invalid json, missing or invalid fields, etc)
225
+    :statuscode 401: Unauthorized
226
+    :statuscode 403: Account is not Active or permission denied
227
+
228
+
229
+.. http:get:: /v1/repositories/(namespace)/(repo_name)/images
230
+
231
+    get the images for a user repo.
232
+
233
+    **Example Request**:
234
+
235
+    .. sourcecode:: http
236
+
237
+        GET /v1/repositories/foo/bar/images HTTP/1.1
238
+        Host: index.docker.io
239
+        Accept: application/json
240
+
241
+    :parameter namespace: the namespace for the repo
242
+    :parameter repo_name: the name for the repo
243
+
244
+    **Example Response**:
245
+
246
+    .. sourcecode:: http
247
+
248
+        HTTP/1.1 200
249
+        Vary: Accept
250
+        Content-Type: application/json
251
+
252
+        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
253
+        “checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”},
254
+        {“id”: “ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds”,
255
+        “checksum”: “34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew”}]
256
+
257
+    :statuscode 200: OK
258
+    :statuscode 404: Not found
259
+
260
+Library Repo Images
261
+~~~~~~~~~~~~~~~~~~~
262
+
263
+.. http:put:: /v1/repositories/(repo_name)/images
264
+
265
+    Update the images for a library repo.
266
+
267
+    **Example Request**:
268
+
269
+    .. sourcecode:: http
270
+
271
+        PUT /v1/repositories/foobar/images HTTP/1.1
272
+        Host: index.docker.io
273
+        Accept: application/json
274
+        Content-Type: application/json
275
+        Authorization: Basic akmklmasadalkm==
276
+
277
+        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
278
+        “checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
279
+
280
+    :parameter repo_name: the library name for the repo
281
+
282
+    **Example Response**:
283
+
284
+    .. sourcecode:: http
285
+
286
+        HTTP/1.1 204
287
+        Vary: Accept
288
+        Content-Type: application/json
289
+
290
+        ""
291
+
292
+    :statuscode 204: Created
293
+    :statuscode 400: Errors (invalid json, missing or invalid fields, etc)
294
+    :statuscode 401: Unauthorized
295
+    :statuscode 403: Account is not Active or permission denied
296
+
297
+
298
+.. http:get:: /v1/repositories/(repo_name)/images
299
+
300
+    get the images for a library repo.
301
+
302
+    **Example Request**:
303
+
304
+    .. sourcecode:: http
305
+
306
+        GET /v1/repositories/foobar/images HTTP/1.1
307
+        Host: index.docker.io
308
+        Accept: application/json
309
+
310
+    :parameter repo_name: the library name for the repo
311
+
312
+    **Example Response**:
313
+
314
+    .. sourcecode:: http
315
+
316
+        HTTP/1.1 200
317
+        Vary: Accept
318
+        Content-Type: application/json
319
+
320
+        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
321
+        “checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”},
322
+        {“id”: “ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds”,
323
+        “checksum”: “34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew”}]
324
+
325
+    :statuscode 200: OK
326
+    :statuscode 404: Not found
327
+
328
+
329
+Repository Authorization
330
+************************
331
+
332
+Library Repo
333
+~~~~~~~~~~~~
334
+
335
+.. http:put:: /v1/repositories/(repo_name)/auth
336
+
337
+    authorize a token for a library repo
338
+
339
+    **Example Request**:
340
+
341
+    .. sourcecode:: http
342
+
343
+        PUT /v1/repositories/foobar/auth HTTP/1.1
344
+        Host: index.docker.io
345
+        Accept: application/json
346
+        Authorization: Token signature=123abc,repository="library/foobar",access=write
347
+
348
+    :parameter repo_name: the library name for the repo
349
+
350
+    **Example Response**:
351
+
352
+    .. sourcecode:: http
353
+
354
+        HTTP/1.1 200
355
+        Vary: Accept
356
+        Content-Type: application/json
357
+
358
+        "OK"
359
+
360
+    :statuscode 200: OK
361
+    :statuscode 403: Permission denied
362
+    :statuscode 404: Not found
363
+
364
+
365
+User Repo
366
+~~~~~~~~~
367
+
368
+.. http:put:: /v1/repositories/(namespace)/(repo_name)/auth
369
+
370
+    authorize a token for a user repo
371
+
372
+    **Example Request**:
373
+
374
+    .. sourcecode:: http
375
+
376
+        PUT /v1/repositories/foo/bar/auth HTTP/1.1
377
+        Host: index.docker.io
378
+        Accept: application/json
379
+        Authorization: Token signature=123abc,repository="foo/bar",access=write
380
+
381
+    :parameter namespace: the namespace for the repo
382
+    :parameter repo_name: the name for the repo
383
+
384
+    **Example Response**:
385
+
386
+    .. sourcecode:: http
387
+
388
+        HTTP/1.1 200
389
+        Vary: Accept
390
+        Content-Type: application/json
391
+
392
+        "OK"
393
+
394
+    :statuscode 200: OK
395
+    :statuscode 403: Permission denied
396
+    :statuscode 404: Not found
397
+
398
+
399
+2.2 Users
400
+^^^^^^^^^
401
+
402
+User Login
403
+**********
404
+
405
+.. http:get:: /v1/users
406
+
407
+    If you want to check your login, you can try this endpoint
408
+    
409
+    **Example Request**:
410
+    
411
+    .. sourcecode:: http
412
+    
413
+        GET /v1/users HTTP/1.1
414
+        Host: index.docker.io
415
+        Accept: application/json
416
+        Authorization: Basic akmklmasadalkm==
417
+
418
+    **Example Response**:
419
+
420
+    .. sourcecode:: http
421
+
422
+        HTTP/1.1 200 OK
423
+        Vary: Accept
424
+        Content-Type: application/json
425
+
426
+        OK
427
+
428
+    :statuscode 200: no error
429
+    :statuscode 401: Unauthorized
430
+    :statuscode 403: Account is not Active
431
+
432
+
433
+User Register
434
+*************
435
+
436
+.. http:post:: /v1/users
437
+
438
+    Registering a new account.
439
+
440
+    **Example request**:
441
+
442
+    .. sourcecode:: http
443
+
444
+        POST /v1/users HTTP/1.1
445
+        Host: index.docker.io
446
+        Accept: application/json
447
+        Content-Type: application/json
448
+
449
+        {"email": "sam@dotcloud.com",
450
+         "password": "toto42",
451
+         "username": "foobar"'}
452
+
453
+    :jsonparameter email: valid email address, that needs to be confirmed
454
+    :jsonparameter username: min 4 character, max 30 characters, must match the regular expression [a-z0-9_].
455
+    :jsonparameter password: min 5 characters
456
+
457
+    **Example Response**:
458
+
459
+    .. sourcecode:: http
460
+
461
+        HTTP/1.1 201 OK
462
+        Vary: Accept
463
+        Content-Type: application/json
464
+
465
+        "User Created"
466
+
467
+    :statuscode 201: User Created
468
+    :statuscode 400: Errors (invalid json, missing or invalid fields, etc)
469
+
470
+Update User
471
+***********
472
+
473
+.. http:put:: /v1/users/(username)/
474
+
475
+    Change a password or email address for given user. If you pass in an email,
476
+    it will add it to your account, it will not remove the old one. Passwords will
477
+    be updated.
478
+
479
+    It is up to the client to verify that that password that is sent is the one that
480
+    they want. Common approach is to have them type it twice.
481
+
482
+    **Example Request**:
483
+
484
+    .. sourcecode:: http
485
+
486
+        PUT /v1/users/fakeuser/ HTTP/1.1
487
+        Host: index.docker.io
488
+        Accept: application/json
489
+        Content-Type: application/json
490
+        Authorization: Basic akmklmasadalkm==
491
+
492
+        {"email": "sam@dotcloud.com",
493
+         "password": "toto42"}
494
+
495
+    :parameter username: username for the person you want to update
496
+
497
+    **Example Response**:
498
+
499
+    .. sourcecode:: http
500
+
501
+        HTTP/1.1 204
502
+        Vary: Accept
503
+        Content-Type: application/json
504
+
505
+        ""
506
+
507
+    :statuscode 204: User Updated
508
+    :statuscode 400: Errors (invalid json, missing or invalid fields, etc)
509
+    :statuscode 401: Unauthorized
510
+    :statuscode 403: Account is not Active
511
+    :statuscode 404: User not found
512
+
513
+
514
+2.3 Search
515
+^^^^^^^^^^
516
+If you need to search the index, this is the endpoint you would use.
517
+
518
+Search
519
+******
520
+
521
+.. http:get:: /v1/search
522
+
523
+   Search the Index given a search term. It accepts :http:method:`get` only.
524
+
525
+   **Example request**:
526
+
527
+   .. sourcecode:: http
528
+
529
+      GET /v1/search?q=search_term HTTP/1.1
530
+      Host: example.com
531
+      Accept: application/json
532
+
533
+
534
+   **Example response**:
535
+
536
+   .. sourcecode:: http
537
+
538
+      HTTP/1.1 200 OK
539
+      Vary: Accept
540
+      Content-Type: application/json
541
+
542
+      {"query":"search_term",
543
+        "num_results": 2,
544
+        "results" : [
545
+           {"name": "dotcloud/base", "description": "A base ubuntu64  image..."},
546
+           {"name": "base2", "description": "A base ubuntu64  image..."},
547
+         ]
548
+       }
549
+
550
+   :query q: what you want to search for
551
+   :statuscode 200: no error
552
+   :statuscode 500: server error
0 553
deleted file mode 100644
... ...
@@ -1,43 +0,0 @@
1
-:title: Docker Index documentation
2
-:description: Documentation for docker Index
3
-:keywords: docker, index, api
4
-
5
-
6
-=======================
7
-Docker Index Search API
8
-=======================
9
-
10
-Search
11
-
12
-.. http:get:: /v1/search
13
-
14
-   Search the Index given a search term. It accepts :http:method:`get` only.
15
-
16
-   **Example request**:
17
-
18
-   .. sourcecode:: http
19
-
20
-      GET /v1/search?q=search_term HTTP/1.1
21
-      Host: example.com
22
-      Accept: application/json
23
-
24
-   **Example response**:
25
-
26
-   .. sourcecode:: http
27
-
28
-      HTTP/1.1 200 OK
29
-      Vary: Accept
30
-      Content-Type: application/json
31
-
32
-      {"query":"search_term",
33
-        "num_results": 2,
34
-        "results" : [
35
-           {"name": "dotcloud/base", "description": "A base ubuntu64  image..."},
36
-           {"name": "base2", "description": "A base ubuntu64  image..."},
37
-         ]
38
-       }
39
-
40
-   :query q: what you want to search for
41
-   :statuscode 200: no error
42
-   :statuscode 500: server error
43 1
\ No newline at end of file
... ...
@@ -1,7 +1,6 @@
1
-:title: Registry Documentation
2
-:description: Documentation for docker Registry and Registry API
3
-:keywords: docker, registry, api, index
4
-
1
+:title: Registry API
2
+:description: API Documentation for Docker Registry
3
+:keywords: API, Docker, index, registry, REST, documentation
5 4
 
6 5
 ===================
7 6
 Docker Registry API
... ...
@@ -9,29 +8,10 @@ Docker Registry API
9 9
 
10 10
 .. contents:: Table of Contents
11 11
 
12
-1. The 3 roles
13
-===============
14
-
15
-1.1 Index
16
-
17
-The Index is responsible for centralizing information about:
18
-- User accounts
19
-- Checksums of the images
20
-- Public namespaces
21
-
22
-The Index has different components:
23
-- Web UI
24
-- Meta-data store (comments, stars, list public repositories)
25
-- Authentication service
26
-- Tokenization
12
+1. Brief introduction
13
+=====================
27 14
 
28
-The index is authoritative for those information.
29
-
30
-We expect that there will be only one instance of the index, run and managed by dotCloud.
31
-
32
-1.2 Registry
15
+- This is the REST API for the Docker Registry
33 16
 - It stores the images and the graph for a set of repositories
34 17
 - It does not have user accounts data
35 18
 - It has no notion of user accounts or authorization
... ...
@@ -60,418 +40,424 @@ We expect that there will be multiple registries out there. To help to grasp the
60 60
 
61 61
 The latter would only require two new commands in docker, e.g. “registryget” and “registryput”, wrapping access to the local filesystem (and optionally doing consistency checks). Authentication and authorization are then delegated to SSH (e.g. with public keys).
62 62
 
63
-1.3 Docker
64
-
65
-On top of being a runtime for LXC, Docker is the Registry client. It supports:
66
-- Push / Pull on the registry
67
-- Client authentication on the Index
68
-
69
-2. Workflow
70
-===========
71
-
72
-2.1 Pull
73
-
74
-.. image:: /static_files/docker_pull_chart.png
75
-
76
-1. Contact the Index to know where I should download “samalba/busybox”
77
-2. Index replies:
78
-   a. “samalba/busybox” is on Registry A
79
-   b. here are the checksums for “samalba/busybox” (for all layers)
80
-   c. token
81
-3. Contact Registry A to receive the layers for “samalba/busybox” (all of them to the base image). Registry A is authoritative for “samalba/busybox” but keeps a copy of all inherited layers and serve them all from the same location.
82
-4. registry contacts index to verify if token/user is allowed to download images
83
-5. Index returns true/false lettings registry know if it should proceed or error out
84
-6. Get the payload for all layers
85
-
86
-It’s possible to run docker pull \https://<registry>/repositories/samalba/busybox. In this case, docker bypasses the Index. However the security is not guaranteed (in case Registry A is corrupted) because there won’t be any checksum checks.
87
-
88
-Currently registry redirects to s3 urls for downloads, going forward all downloads need to be streamed through the registry. The Registry will then abstract the calls to S3 by a top-level class which implements sub-classes for S3 and local storage.
89
-
90
-Token is only returned when the 'X-Docker-Token' header is sent with request.
91
-
92
-Basic Auth is required to pull private repos. Basic auth isn't required for pulling public repos, but if one is provided, it needs to be valid and for an active account.
93
-
94
-API (pulling repository foo/bar):
95
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
96
-
97
-1. (Docker -> Index) GET /v1/repositories/foo/bar/images
98
-    **Headers**:
99
-        Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
100
-        X-Docker-Token: true
101
-    **Action**:
102
-        (looking up the foo/bar in db and gets images and checksums for that repo (all if no tag is specified, if tag, only checksums for those tags) see part 4.4.1)
103
-
104
-2. (Index -> Docker) HTTP 200 OK
105
-
106
-    **Headers**:
107
-        - Authorization: Token signature=123abc,repository=”foo/bar”,access=write
108
-        - X-Docker-Endpoints: registry.docker.io [, registry2.docker.io]
109
-    **Body**:
110
-        Jsonified checksums (see part 4.4.1)
111
-
112
-3. (Docker -> Registry) GET /v1/repositories/foo/bar/tags/latest
113
-    **Headers**:
114
-        Authorization: Token signature=123abc,repository=”foo/bar”,access=write
115
-
116
-4. (Registry -> Index) GET /v1/repositories/foo/bar/images
117
-
118
-    **Headers**:
119
-        Authorization: Token signature=123abc,repository=”foo/bar”,access=read
120
-
121
-    **Body**:
122
-        <ids and checksums in payload>
123
-
124
-    **Action**:
125
-        ( Lookup token see if they have access to pull.)
126
-
127
-        If good:
128
-            HTTP 200 OK
129
-            Index will invalidate the token
130
-        If bad:
131
-            HTTP 401 Unauthorized
132
-
133
-5. (Docker -> Registry) GET /v1/images/928374982374/ancestry
134
-    **Action**:
135
-        (for each image id returned in the registry, fetch /json + /layer)
63
+2. Endpoints
64
+============
136 65
 
137
-.. note::
138
-
139
-    If someone makes a second request, then we will always give a new token, never reuse tokens.
140
-
141
-2.2 Push
142
-
143
-.. image:: /static_files/docker_push_chart.png
144
-
145
-1. Contact the index to allocate the repository name “samalba/busybox” (authentication required with user credentials)
146
-2. If authentication works and namespace available, “samalba/busybox” is allocated and a temporary token is returned (namespace is marked as initialized in index)
147
-3. Push the image on the registry (along with the token)
148
-4. Registry A contacts the Index to verify the token (token must corresponds to the repository name)
149
-5. Index validates the token. Registry A starts reading the stream pushed by docker and store the repository (with its images)
150
-6. docker contacts the index to give checksums for upload images
151
-
152
-.. note::
153
-
154
-    **It’s possible not to use the Index at all!** In this case, a deployed version of the Registry is deployed to store and serve images. Those images are not authentified and the security is not guaranteed.
155
-
156
-.. note::
157
-
158
-    **Index can be replaced!** For a private Registry deployed, a custom Index can be used to serve and validate token according to different policies.
159
-
160
-Docker computes the checksums and submit them to the Index at the end of the push. When a repository name does not have checksums on the Index, it means that the push is in progress (since checksums are submitted at the end).
66
+2.1 Images
67
+----------
161 68
 
162
-API (pushing repos foo/bar):
163
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
69
+Layer
70
+*****
164 71
 
165
-1. (Docker -> Index) PUT /v1/repositories/foo/bar/
166
-    **Headers**:
167
-        Authorization: Basic sdkjfskdjfhsdkjfh==
168
-        X-Docker-Token: true
72
+.. http:get:: /v1/images/(image_id)/layer 
169 73
 
170
-    **Action**::
171
-        - in index, we allocated a new repository, and set to initialized
74
+    get image layer for a given ``image_id``
172 75
 
173
-    **Body**::
174
-        (The body contains the list of images that are going to be pushed, with empty checksums. The checksums will be set at the end of the push)::
76
+    **Example Request**:
175 77
 
176
-        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
78
+    .. sourcecode:: http
177 79
 
178
-2. (Index -> Docker) 200 Created
179
-    **Headers**:
180
-        - WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=write
181
-        - X-Docker-Endpoints: registry.docker.io [, registry2.docker.io]
80
+        GET /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/layer HTTP/1.1
81
+        Host: registry-1.docker.io
82
+        Accept: application/json
83
+        Content-Type: application/json
84
+        Authorization: Token akmklmasadalkmsdfgsdgdge33
182 85
 
183
-3. (Docker -> Registry) PUT /v1/images/98765432_parent/json
184
-    **Headers**:
185
-        Authorization: Token signature=123abc,repository=”foo/bar”,access=write
86
+    :parameter image_id: the id for the layer you want to get
186 87
 
187
-4. (Registry->Index) GET /v1/repositories/foo/bar/images
188
-    **Headers**:
189
-        Authorization: Token signature=123abc,repository=”foo/bar”,access=write
190
-    **Action**::
191
-        - Index:
192
-            will invalidate the token.
193
-        - Registry:
194
-            grants a session (if token is approved) and fetches the images id
88
+    **Example Response**:
195 89
 
196
-5. (Docker -> Registry) PUT /v1/images/98765432_parent/json
197
-    **Headers**::
198
-        - Authorization: Token signature=123abc,repository=”foo/bar”,access=write
199
-        - Cookie: (Cookie provided by the Registry)
90
+    .. sourcecode:: http
200 91
 
201
-6. (Docker -> Registry) PUT /v1/images/98765432/json
202
-    **Headers**:
92
+        HTTP/1.1 200
93
+        Vary: Accept
94
+        Content-Type: application/json
203 95
         Cookie: (Cookie provided by the Registry)
204 96
 
205
-7. (Docker -> Registry) PUT /v1/images/98765432_parent/layer
206
-    **Headers**:
97
+        {
98
+            id: "088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c",
99
+            parent: "aeee6396d62273d180a49c96c62e45438d87c7da4a5cf5d2be6bee4e21bc226f",
100
+            created: "2013-04-30T17:46:10.843673+03:00",
101
+            container: "8305672a76cc5e3d168f97221106ced35a76ec7ddbb03209b0f0d96bf74f6ef7",
102
+            container_config: {
103
+                Hostname: "host-test",
104
+                User: "",
105
+                Memory: 0,
106
+                MemorySwap: 0,
107
+                AttachStdin: false,
108
+                AttachStdout: false,
109
+                AttachStderr: false,
110
+                PortSpecs: null,
111
+                Tty: false,
112
+                OpenStdin: false,
113
+                StdinOnce: false,
114
+                Env: null,
115
+                Cmd: [
116
+                "/bin/bash",
117
+                "-c",
118
+                "apt-get -q -yy -f install libevent-dev"
119
+                ],
120
+                Dns: null,
121
+                Image: "imagename/blah",
122
+                Volumes: { },
123
+                VolumesFrom: ""
124
+            },
125
+            docker_version: "0.1.7"
126
+        }
127
+
128
+    :statuscode 200: OK
129
+    :statuscode 401: Requires authorization
130
+    :statuscode 404: Image not found
131
+
132
+
133
+.. http:put:: /v1/images/(image_id)/layer 
134
+
135
+    put image layer for a given ``image_id``
136
+
137
+    **Example Request**:
138
+
139
+    .. sourcecode:: http
140
+
141
+        PUT /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/layer HTTP/1.1
142
+        Host: registry-1.docker.io
143
+        Accept: application/json
144
+        Content-Type: application/json
145
+        Authorization: Token akmklmasadalkmsdfgsdgdge33
146
+
147
+        {
148
+            id: "088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c",
149
+            parent: "aeee6396d62273d180a49c96c62e45438d87c7da4a5cf5d2be6bee4e21bc226f",
150
+            created: "2013-04-30T17:46:10.843673+03:00",
151
+            container: "8305672a76cc5e3d168f97221106ced35a76ec7ddbb03209b0f0d96bf74f6ef7",
152
+            container_config: {
153
+                Hostname: "host-test",
154
+                User: "",
155
+                Memory: 0,
156
+                MemorySwap: 0,
157
+                AttachStdin: false,
158
+                AttachStdout: false,
159
+                AttachStderr: false,
160
+                PortSpecs: null,
161
+                Tty: false,
162
+                OpenStdin: false,
163
+                StdinOnce: false,
164
+                Env: null,
165
+                Cmd: [
166
+                "/bin/bash",
167
+                "-c",
168
+                "apt-get -q -yy -f install libevent-dev"
169
+                ],
170
+                Dns: null,
171
+                Image: "imagename/blah",
172
+                Volumes: { },
173
+                VolumesFrom: ""
174
+            },
175
+            docker_version: "0.1.7"
176
+        }
177
+
178
+    :parameter image_id: the id for the layer you want to get
179
+
180
+
181
+    **Example Response**:
182
+
183
+    .. sourcecode:: http
184
+    
185
+        HTTP/1.1 200
186
+        Vary: Accept
187
+        Content-Type: application/json
188
+
189
+        ""
190
+
191
+    :statuscode 200: OK
192
+    :statuscode 401: Requires authorization
193
+    :statuscode 404: Image not found
194
+
195
+
196
+Image
197
+*****
198
+
199
+.. http:put:: /v1/images/(image_id)/json
200
+
201
+    put image for a given ``image_id``
202
+
203
+    **Example Request**:
204
+
205
+    .. sourcecode:: http
206
+
207
+        PUT /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/json HTTP/1.1
208
+        Host: registry-1.docker.io
209
+        Accept: application/json
210
+        Content-Type: application/json
207 211
         Cookie: (Cookie provided by the Registry)
208 212
 
209
-8. (Docker -> Registry) PUT /v1/images/98765432/layer
210
-    **Headers**:
211
-        X-Docker-Checksum: sha256:436745873465fdjkhdfjkgh
213
+        {
214
+         “id”: “088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c”,
215
+         “checksum”:  “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
216
+         }
212 217
 
213
-9. (Docker -> Registry) PUT /v1/repositories/foo/bar/tags/latest
214
-    **Headers**:
215
-        Cookie: (Cookie provided by the Registry)
216
-    **Body**:
217
-        “98765432”
218
+    :parameter image_id: the id for the layer you want to get
218 219
 
219
-10. (Docker -> Index) PUT /v1/repositories/foo/bar/images
220 220
 
221
-    **Headers**:
222
-        Authorization: Basic 123oislifjsldfj==
223
-        X-Docker-Endpoints: registry1.docker.io (no validation on this right now)
221
+    **Example Response**:
224 222
 
225
-    **Body**:
226
-        (The image, id’s, tags and checksums)
223
+    .. sourcecode:: http
224
+    
225
+        HTTP/1.1 200
226
+        Vary: Accept
227
+        Content-Type: application/json
227 228
 
228
-        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
229
-        “checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
229
+        ""
230 230
 
231
-    **Return** HTTP 204
231
+    :statuscode 200: OK
232
+    :statuscode 401: Requires authorization
232 233
 
233
-.. note::
234
+.. http:get:: /v1/images/(image_id)/json
234 235
 
235
-     If push fails and they need to start again, what happens in the index, there will already be a record for the namespace/name, but it will be initialized. Should we allow it, or mark as name already used? One edge case could be if someone pushes the same thing at the same time with two different shells.
236
+    get image for a given ``image_id``
236 237
 
237
-     If it's a retry on the Registry, Docker has a cookie (provided by the registry after token validation). So the Index won’t have to provide a new token.
238
+    **Example Request**:
238 239
 
239
-3. How to use the Registry in standalone mode
240
-=============================================
240
+    .. sourcecode:: http
241 241
 
242
-The Index has two main purposes (along with its fancy social features):
242
+        GET /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/json HTTP/1.1
243
+        Host: registry-1.docker.io
244
+        Accept: application/json
245
+        Content-Type: application/json
246
+        Cookie: (Cookie provided by the Registry)
243 247
 
244
-- Resolve short names (to avoid passing absolute URLs all the time)
245
-   - username/projectname -> \https://registry.docker.io/users/<username>/repositories/<projectname>/
246
-- Authenticate a user as a repos owner (for a central referenced repository)
248
+    :parameter image_id: the id for the layer you want to get
247 249
 
248
-3.1 Without an Index
249
-Using the Registry without the Index can be useful to store the images on a private network without having to rely on an external entity controlled by dotCloud.
250
+    **Example Response**:
250 251
 
251
-In this case, the registry will be launched in a special mode (--standalone? --no-index?). In this mode, the only thing which changes is that Registry will never contact the Index to verify a token. It will be the Registry owner responsibility to authenticate the user who pushes (or even pulls) an image using any mechanism (HTTP auth, IP based, etc...).
252
+    .. sourcecode:: http
252 253
 
253
-In this scenario, the Registry is responsible for the security in case of data corruption since the checksums are not delivered by a trusted entity.
254
+        HTTP/1.1 200
255
+        Vary: Accept
256
+        Content-Type: application/json
254 257
 
255
-As hinted previously, a standalone registry can also be implemented by any HTTP server handling GET/PUT requests (or even only GET requests if no write access is necessary).
258
+        {
259
+         “id”: “088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c”,
260
+         “checksum”:  “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
261
+         }
256 262
 
257
-3.2 With an Index
263
+    :statuscode 200: OK
264
+    :statuscode 401: Requires authorization
265
+    :statuscode 404: Image not found
258 266
 
259
-The Index data needed by the Registry are simple:
260
-- Serve the checksums
261
-- Provide and authorize a Token
262 267
 
263
-In the scenario of a Registry running on a private network with the need of centralizing and authorizing, it’s easy to use a custom Index.
268
+Ancestry
269
+********
264 270
 
265
-The only challenge will be to tell Docker to contact (and trust) this custom Index. Docker will be configurable at some point to use a specific Index, it’ll be the private entity responsibility (basically the organization who uses Docker in a private environment) to maintain the Index and the Docker’s configuration among its consumers.
271
+.. http:get:: /v1/images/(image_id)/ancestry
266 272
 
267
-4. The API
268
-==========
273
+    get ancestry for an image given an ``image_id``
269 274
 
270
-The first version of the api is available here: https://github.com/jpetazzo/docker/blob/acd51ecea8f5d3c02b00a08176171c59442df8b3/docs/images-repositories-push-pull.md
275
+    **Example Request**:
271 276
 
272
-4.1 Images
277
+    .. sourcecode:: http
273 278
 
274
-The format returned in the images is not defined here (for layer and json), basically because Registry stores exactly the same kind of information as Docker uses to manage them.
275
-
276
-The format of ancestry is a line-separated list of image ids, in age order. I.e. the image’s parent is on the last line, the parent of the parent on the next-to-last line, etc.; if the image has no parent, the file is empty.
279
+        GET /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/ancestry HTTP/1.1
280
+        Host: registry-1.docker.io
281
+        Accept: application/json
282
+        Content-Type: application/json
283
+        Cookie: (Cookie provided by the Registry)
277 284
 
278
-GET /v1/images/<image_id>/layer
279
-PUT /v1/images/<image_id>/layer
280
-GET /v1/images/<image_id>/json
281
-PUT /v1/images/<image_id>/json
282
-GET /v1/images/<image_id>/ancestry
283
-PUT /v1/images/<image_id>/ancestry
285
+    :parameter image_id: the id for the layer you want to get
284 286
 
285
-4.2 Users
287
+    **Example Response**:
286 288
 
287
-4.2.1 Create a user (Index)
288
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
289
+    .. sourcecode:: http
289 290
 
290
-POST /v1/users
291
+        HTTP/1.1 200
292
+        Vary: Accept
293
+        Content-Type: application/json
291 294
 
292
-**Body**:
293
-    {"email": "sam@dotcloud.com", "password": "toto42", "username": "foobar"'}
295
+        ["088b4502f51920fbd9b7c503e87c7a2c05aa3adc3d35e79c031fa126b403200f",
296
+         "aeee63968d87c7da4a5cf5d2be6bee4e21bc226fd62273d180a49c96c62e4543",
297
+         "bfa4c5326bc764280b0863b46a4b20d940bc1897ef9c1dfec060604bdc383280",
298
+         "6ab5893c6927c15a15665191f2c6cf751f5056d8b95ceee32e43c5e8a3648544"]
294 299
 
295
-**Validation**:
296
-    - **username** : min 4 character, max 30 characters, must match the regular expression [a-z0-9_].
297
-    - **password**: min 5 characters
300
+    :statuscode 200: OK
301
+    :statuscode 401: Requires authorization
302
+    :statuscode 404: Image not found
298 303
 
299
-**Valid**: return HTTP 200
300 304
 
301
-Errors: HTTP 400 (we should create error codes for possible errors)
302
-- invalid json
303
-- missing field
304
-- wrong format (username, password, email, etc)
305
-- forbidden name
306
-- name already exists
305
+2.2 Tags
306
+--------
307 307
 
308
-.. note::
308
+.. http:get:: /v1/repositories/(namespace)/(repository)/tags
309 309
 
310
-    A user account will be valid only if the email has been validated (a validation link is sent to the email address).
310
+    get all of the tags for the given repo.
311 311
 
312
-4.2.2 Update a user (Index)
313
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
312
+    **Example Request**:
314 313
 
315
-PUT /v1/users/<username>
314
+    .. sourcecode:: http
316 315
 
317
-**Body**:
318
-    {"password": "toto"}
316
+        GET /v1/repositories/foo/bar/tags HTTP/1.1
317
+        Host: registry-1.docker.io
318
+        Accept: application/json
319
+        Content-Type: application/json
320
+        Cookie: (Cookie provided by the Registry)
319 321
 
320
-.. note::
322
+    :parameter namespace: namespace for the repo
323
+    :parameter repository: name for the repo
321 324
 
322
-    We can also update email address, if they do, they will need to reverify their new email address.
325
+    **Example Response**:
323 326
 
324
-4.2.3 Login (Index)
325
-^^^^^^^^^^^^^^^^^^^
326
-Does nothing else but asking for a user authentication. Can be used to validate credentials. HTTP Basic Auth for now, maybe change in future.
327
+    .. sourcecode:: http
327 328
 
328
-GET /v1/users
329
+        HTTP/1.1 200
330
+        Vary: Accept
331
+        Content-Type: application/json
329 332
 
330
-**Return**:
331
-    - Valid: HTTP 200
332
-    - Invalid login: HTTP 401
333
-    - Account inactive: HTTP 403 Account is not Active
333
+        {
334
+            "latest": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
335
+            “0.1.1”:  “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
336
+        }
334 337
 
335
-4.3 Tags (Registry)
338
+    :statuscode 200: OK
339
+    :statuscode 401: Requires authorization
340
+    :statuscode 404: Repository not found
336 341
 
337
-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.
338 342
 
339
-The following naming restrictions apply:
343
+.. http:get:: /v1/repositories/(namespace)/(repository)/tags/(tag)
340 344
 
341
-- Namespaces must match the same regular expression as usernames (See 4.2.1.)
342
-- Repository names must match the regular expression [a-zA-Z0-9-_.]
345
+    get a tag for the given repo.
343 346
 
344
-4.3.1 Get all tags
345
-^^^^^^^^^^^^^^^^^^
347
+    **Example Request**:
346 348
 
347
-GET /v1/repositories/<namespace>/<repository_name>/tags
349
+    .. sourcecode:: http
348 350
 
349
-**Return**: HTTP 200
350
-    {
351
-    "latest": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
352
-    “0.1.1”:  “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
353
-    }
351
+        GET /v1/repositories/foo/bar/tags/latest HTTP/1.1
352
+        Host: registry-1.docker.io
353
+        Accept: application/json
354
+        Content-Type: application/json
355
+        Cookie: (Cookie provided by the Registry)
354 356
 
355
-4.3.2 Read the content of a tag (resolve the image id)
356
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
357
+    :parameter namespace: namespace for the repo
358
+    :parameter repository: name for the repo
359
+    :parameter tag: name of tag you want to get
357 360
 
358
-GET /v1/repositories/<namespace>/<repo_name>/tags/<tag>
361
+    **Example Response**:
359 362
 
360
-**Return**:
361
-    "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
363
+    .. sourcecode:: http
362 364
 
363
-4.3.3 Delete a tag (registry)
364
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
365
+        HTTP/1.1 200
366
+        Vary: Accept
367
+        Content-Type: application/json
365 368
 
366
-DELETE /v1/repositories/<namespace>/<repo_name>/tags/<tag>
369
+        "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
367 370
 
368
-4.4 Images (Index)
371
+    :statuscode 200: OK
372
+    :statuscode 401: Requires authorization
373
+    :statuscode 404: Tag not found
369 374
 
370
-For the Index to “resolve” the repository name to a Registry location, it uses the X-Docker-Endpoints header. In other terms, this requests always add a “X-Docker-Endpoints” to indicate the location of the registry which hosts this repository.
375
+.. http:delete:: /v1/repositories/(namespace)/(repository)/tags/(tag)
371 376
 
372
-4.4.1 Get the images
373
-^^^^^^^^^^^^^^^^^^^^^
377
+    delete the tag for the repo
374 378
 
375
-GET /v1/repositories/<namespace>/<repo_name>/images
379
+    **Example Request**:
376 380
 
377
-**Return**: HTTP 200
378
-    [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”, “checksum”: “md5:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
381
+    .. sourcecode:: http
379 382
 
383
+        DELETE /v1/repositories/foo/bar/tags/latest HTTP/1.1
384
+        Host: registry-1.docker.io
385
+        Accept: application/json
386
+        Content-Type: application/json
387
+        Cookie: (Cookie provided by the Registry)
380 388
 
381
-4.4.2 Add/update the images
382
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
389
+    :parameter namespace: namespace for the repo
390
+    :parameter repository: name for the repo
391
+    :parameter tag: name of tag you want to delete
383 392
 
384
-You always add images, you never remove them.
393
+    **Example Response**:
385 394
 
386
-PUT /v1/repositories/<namespace>/<repo_name>/images
395
+    .. sourcecode:: http
387 396
 
388
-**Body**:
389
-    [ {“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”, “checksum”: “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”} ]
397
+        HTTP/1.1 200
398
+        Vary: Accept
399
+        Content-Type: application/json
390 400
 
391
-**Return** 204
401
+        ""
392 402
 
393
-5. Chaining Registries
394
-======================
403
+    :statuscode 200: OK
404
+    :statuscode 401: Requires authorization
405
+    :statuscode 404: Tag not found
395 406
 
396
-It’s possible to chain Registries server for several reasons:
397
-- Load balancing
398
-- Delegate the next request to another server
399 407
 
400
-When a Registry is a reference for a repository, it should host the entire images chain in order to avoid breaking the chain during the download.
408
+.. http:put:: /v1/repositories/(namespace)/(repository)/tags/(tag)
401 409
 
402
-The Index and Registry use this mechanism to redirect on one or the other.
410
+    put a tag for the given repo.
403 411
 
404
-Example with an image download:
405
-On every request, a special header can be returned:
412
+    **Example Request**:
406 413
 
407
-X-Docker-Endpoints: server1,server2
414
+    .. sourcecode:: http
408 415
 
409
-On the next request, the client will always pick a server from this list.
416
+        PUT /v1/repositories/foo/bar/tags/latest HTTP/1.1
417
+        Host: registry-1.docker.io
418
+        Accept: application/json
419
+        Content-Type: application/json
420
+        Cookie: (Cookie provided by the Registry)
410 421
 
411
-6. Authentication & Authorization
412
-=================================
422
+        “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”
413 423
 
414
-6.1 On the Index
424
+    :parameter namespace: namespace for the repo
425
+    :parameter repository: name for the repo
426
+    :parameter tag: name of tag you want to add
415 427
 
416
-The Index supports both “Basic” and “Token” challenges. Usually when there is a “401 Unauthorized”, the Index replies this::
428
+    **Example Response**:
417 429
 
418
-    401 Unauthorized
419
-    WWW-Authenticate: Basic realm="auth required",Token
430
+    .. sourcecode:: http
420 431
 
421
-You have 3 options:
432
+        HTTP/1.1 200
433
+        Vary: Accept
434
+        Content-Type: application/json
422 435
 
423
-1. Provide user credentials and ask for a token
436
+        ""
424 437
 
425
-    **Header**:
426
-        - Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
427
-        - X-Docker-Token: true
438
+    :statuscode 200: OK
439
+    :statuscode 400: Invalid data
440
+    :statuscode 401: Requires authorization
441
+    :statuscode 404: Image not found
428 442
 
429
-    In this case, along with the 200 response, you’ll get a new token (if user auth is ok):
430
-    If authorization isn't correct you get a 401 response.
431
-    If account isn't active you will get a 403 response.
443
+2.3 Repositories
444
+----------------
432 445
 
433
-    **Response**:
434
-        - 200 OK
435
-        - X-Docker-Token: Token signature=123abc,repository=”foo/bar”,access=read
446
+.. http:delete:: /v1/repositories/(namespace)/(repository)/
436 447
 
437
-2. Provide user credentials only
448
+    delete a repository
438 449
 
439
-    **Header**:
440
-        Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
450
+    **Example Request**:
441 451
 
442
-3. Provide Token
452
+    .. sourcecode:: http
443 453
 
444
-    **Header**:
445
-        Authorization: Token signature=123abc,repository=”foo/bar”,access=read
454
+        DELETE /v1/repositories/foo/bar/ HTTP/1.1
455
+        Host: registry-1.docker.io
456
+        Accept: application/json
457
+        Content-Type: application/json
458
+        Cookie: (Cookie provided by the Registry)
446 459
 
447
-6.2 On the Registry
460
+        ""
448 461
 
449
-The Registry only supports the Token challenge::
462
+    :parameter namespace: namespace for the repo
463
+    :parameter repository: name for the repo
450 464
 
451
-    401 Unauthorized
452
-    WWW-Authenticate: Token
465
+    **Example Response**:
453 466
 
454
-The only way is to provide a token on “401 Unauthorized” responses::
467
+    .. sourcecode:: http
455 468
 
456
-    Authorization: Token signature=123abc,repository=”foo/bar”,access=read
469
+        HTTP/1.1 200
470
+        Vary: Accept
471
+        Content-Type: application/json
457 472
 
458
-Usually, the Registry provides a Cookie when a Token verification succeeded. Every time the Registry passes a Cookie, you have to pass it back the same cookie.::
473
+        ""
459 474
 
460
-    200 OK
461
-    Set-Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=&timestamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="; Path=/; HttpOnly
475
+    :statuscode 200: OK
476
+    :statuscode 401: Requires authorization
477
+    :statuscode 404: Repository not found
462 478
 
463
-Next request::
479
+3.0 Authorization
480
+=================
481
+This is where we describe the authorization process, including the tokens and cookies. 
464 482
 
465
-    GET /(...)
466
-    Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=&timestamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="
483
+TODO: add more info.
467 484
new file mode 100644
... ...
@@ -0,0 +1,569 @@
0
+:title: Registry Documentation
1
+:description: Documentation for docker Registry and Registry API
2
+:keywords: docker, registry, api, index
3
+
4
+
5
+=====================
6
+Registry & index Spec
7
+=====================
8
+
9
+.. contents:: Table of Contents
10
+
11
+1. The 3 roles
12
+===============
13
+
14
+1.1 Index
15
+---------
16
+
17
+The Index is responsible for centralizing information about:
18
+- User accounts
19
+- Checksums of the images
20
+- Public namespaces
21
+
22
+The Index has different components:
23
+- Web UI
24
+- Meta-data store (comments, stars, list public repositories)
25
+- Authentication service
26
+- Tokenization
27
+
28
+The index is authoritative for those information.
29
+
30
+We expect that there will be only one instance of the index, run and managed by dotCloud.
31
+
32
+1.2 Registry
33
+------------
34
+- It stores the images and the graph for a set of repositories
35
+- It does not have user accounts data
36
+- It has no notion of user accounts or authorization
37
+- It delegates authentication and authorization to the Index Auth service using tokens
38
+- It supports different storage backends (S3, cloud files, local FS)
39
+- It doesn’t have a local database
40
+- It will be open-sourced at some point
41
+
42
+We expect that there will be multiple registries out there. To help to grasp the context, here are some examples of registries:
43
+
44
+- **sponsor registry**: such a registry is provided by a third-party hosting infrastructure as a convenience for their customers and the docker community as a whole. Its costs are supported by the third party, but the management and operation of the registry are supported by dotCloud. It features read/write access, and delegates authentication and authorization to the Index.
45
+- **mirror registry**: such a registry is provided by a third-party hosting infrastructure but is targeted at their customers only. Some mechanism (unspecified to date) ensures that public images are pulled from a sponsor registry to the mirror registry, to make sure that the customers of the third-party provider can “docker pull” those images locally.
46
+- **vendor registry**: such a registry is provided by a software vendor, who wants to distribute docker images. It would be operated and managed by the vendor. Only users authorized by the vendor would be able to get write access. Some images would be public (accessible for anyone), others private (accessible only for authorized users). Authentication and authorization would be delegated to the Index. The goal of vendor registries is to let someone do “docker pull basho/riak1.3” and automatically push from the vendor registry (instead of a sponsor registry); i.e. get all the convenience of a sponsor registry, while retaining control on the asset distribution.
47
+- **private registry**: such a registry is located behind a firewall, or protected by an additional security layer (HTTP authorization, SSL client-side certificates, IP address authorization...). The registry is operated by a private entity, outside of dotCloud’s control. It can optionally delegate additional authorization to the Index, but it is not mandatory.
48
+
49
+.. note::
50
+
51
+    Mirror registries and private registries which do not use the Index don’t even need to run the registry code. They can be implemented by any kind of transport implementing HTTP GET and PUT. Read-only registries can be powered by a simple static HTTP server.
52
+
53
+.. note::
54
+
55
+    The latter implies that while HTTP is the protocol of choice for a registry, multiple schemes are possible (and in some cases, trivial):
56
+        - HTTP with GET (and PUT for read-write registries);
57
+        - local mount point;
58
+        - remote docker addressed through SSH.
59
+
60
+The latter would only require two new commands in docker, e.g. “registryget” and “registryput”, wrapping access to the local filesystem (and optionally doing consistency checks). Authentication and authorization are then delegated to SSH (e.g. with public keys).
61
+
62
+1.3 Docker
63
+----------
64
+
65
+On top of being a runtime for LXC, Docker is the Registry client. It supports:
66
+- Push / Pull on the registry
67
+- Client authentication on the Index
68
+
69
+2. Workflow
70
+===========
71
+
72
+2.1 Pull
73
+--------
74
+
75
+.. image:: /static_files/docker_pull_chart.png
76
+
77
+1. Contact the Index to know where I should download “samalba/busybox”
78
+2. Index replies:
79
+   a. “samalba/busybox” is on Registry A
80
+   b. here are the checksums for “samalba/busybox” (for all layers)
81
+   c. token
82
+3. Contact Registry A to receive the layers for “samalba/busybox” (all of them to the base image). Registry A is authoritative for “samalba/busybox” but keeps a copy of all inherited layers and serve them all from the same location.
83
+4. registry contacts index to verify if token/user is allowed to download images
84
+5. Index returns true/false lettings registry know if it should proceed or error out
85
+6. Get the payload for all layers
86
+
87
+It’s possible to run docker pull \https://<registry>/repositories/samalba/busybox. In this case, docker bypasses the Index. However the security is not guaranteed (in case Registry A is corrupted) because there won’t be any checksum checks.
88
+
89
+Currently registry redirects to s3 urls for downloads, going forward all downloads need to be streamed through the registry. The Registry will then abstract the calls to S3 by a top-level class which implements sub-classes for S3 and local storage.
90
+
91
+Token is only returned when the 'X-Docker-Token' header is sent with request.
92
+
93
+Basic Auth is required to pull private repos. Basic auth isn't required for pulling public repos, but if one is provided, it needs to be valid and for an active account.
94
+
95
+API (pulling repository foo/bar):
96
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
97
+
98
+1. (Docker -> Index) GET /v1/repositories/foo/bar/images
99
+    **Headers**:
100
+        Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
101
+        X-Docker-Token: true
102
+    **Action**:
103
+        (looking up the foo/bar in db and gets images and checksums for that repo (all if no tag is specified, if tag, only checksums for those tags) see part 4.4.1)
104
+
105
+2. (Index -> Docker) HTTP 200 OK
106
+
107
+    **Headers**:
108
+        - Authorization: Token signature=123abc,repository=”foo/bar”,access=write
109
+        - X-Docker-Endpoints: registry.docker.io [, registry2.docker.io]
110
+    **Body**:
111
+        Jsonified checksums (see part 4.4.1)
112
+
113
+3. (Docker -> Registry) GET /v1/repositories/foo/bar/tags/latest
114
+    **Headers**:
115
+        Authorization: Token signature=123abc,repository=”foo/bar”,access=write
116
+
117
+4. (Registry -> Index) GET /v1/repositories/foo/bar/images
118
+
119
+    **Headers**:
120
+        Authorization: Token signature=123abc,repository=”foo/bar”,access=read
121
+
122
+    **Body**:
123
+        <ids and checksums in payload>
124
+
125
+    **Action**:
126
+        ( Lookup token see if they have access to pull.)
127
+
128
+        If good:
129
+            HTTP 200 OK
130
+            Index will invalidate the token
131
+        If bad:
132
+            HTTP 401 Unauthorized
133
+
134
+5. (Docker -> Registry) GET /v1/images/928374982374/ancestry
135
+    **Action**:
136
+        (for each image id returned in the registry, fetch /json + /layer)
137
+
138
+.. note::
139
+
140
+    If someone makes a second request, then we will always give a new token, never reuse tokens.
141
+
142
+2.2 Push
143
+--------
144
+
145
+.. image:: /static_files/docker_push_chart.png
146
+
147
+1. Contact the index to allocate the repository name “samalba/busybox” (authentication required with user credentials)
148
+2. If authentication works and namespace available, “samalba/busybox” is allocated and a temporary token is returned (namespace is marked as initialized in index)
149
+3. Push the image on the registry (along with the token)
150
+4. Registry A contacts the Index to verify the token (token must corresponds to the repository name)
151
+5. Index validates the token. Registry A starts reading the stream pushed by docker and store the repository (with its images)
152
+6. docker contacts the index to give checksums for upload images
153
+
154
+.. note::
155
+
156
+    **It’s possible not to use the Index at all!** In this case, a deployed version of the Registry is deployed to store and serve images. Those images are not authentified and the security is not guaranteed.
157
+
158
+.. note::
159
+
160
+    **Index can be replaced!** For a private Registry deployed, a custom Index can be used to serve and validate token according to different policies.
161
+
162
+Docker computes the checksums and submit them to the Index at the end of the push. When a repository name does not have checksums on the Index, it means that the push is in progress (since checksums are submitted at the end).
163
+
164
+API (pushing repos foo/bar):
165
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
166
+
167
+1. (Docker -> Index) PUT /v1/repositories/foo/bar/
168
+    **Headers**:
169
+        Authorization: Basic sdkjfskdjfhsdkjfh==
170
+        X-Docker-Token: true
171
+
172
+    **Action**::
173
+        - in index, we allocated a new repository, and set to initialized
174
+
175
+    **Body**::
176
+        (The body contains the list of images that are going to be pushed, with empty checksums. The checksums will be set at the end of the push)::
177
+
178
+        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
179
+
180
+2. (Index -> Docker) 200 Created
181
+    **Headers**:
182
+        - WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=write
183
+        - X-Docker-Endpoints: registry.docker.io [, registry2.docker.io]
184
+
185
+3. (Docker -> Registry) PUT /v1/images/98765432_parent/json
186
+    **Headers**:
187
+        Authorization: Token signature=123abc,repository=”foo/bar”,access=write
188
+
189
+4. (Registry->Index) GET /v1/repositories/foo/bar/images
190
+    **Headers**:
191
+        Authorization: Token signature=123abc,repository=”foo/bar”,access=write
192
+    **Action**::
193
+        - Index:
194
+            will invalidate the token.
195
+        - Registry:
196
+            grants a session (if token is approved) and fetches the images id
197
+
198
+5. (Docker -> Registry) PUT /v1/images/98765432_parent/json
199
+    **Headers**::
200
+        - Authorization: Token signature=123abc,repository=”foo/bar”,access=write
201
+        - Cookie: (Cookie provided by the Registry)
202
+
203
+6. (Docker -> Registry) PUT /v1/images/98765432/json
204
+    **Headers**:
205
+        Cookie: (Cookie provided by the Registry)
206
+
207
+7. (Docker -> Registry) PUT /v1/images/98765432_parent/layer
208
+    **Headers**:
209
+        Cookie: (Cookie provided by the Registry)
210
+
211
+8. (Docker -> Registry) PUT /v1/images/98765432/layer
212
+    **Headers**:
213
+        X-Docker-Checksum: sha256:436745873465fdjkhdfjkgh
214
+
215
+9. (Docker -> Registry) PUT /v1/repositories/foo/bar/tags/latest
216
+    **Headers**:
217
+        Cookie: (Cookie provided by the Registry)
218
+    **Body**:
219
+        “98765432”
220
+
221
+10. (Docker -> Index) PUT /v1/repositories/foo/bar/images
222
+
223
+    **Headers**:
224
+        Authorization: Basic 123oislifjsldfj==
225
+        X-Docker-Endpoints: registry1.docker.io (no validation on this right now)
226
+
227
+    **Body**:
228
+        (The image, id’s, tags and checksums)
229
+
230
+        [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
231
+        “checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
232
+
233
+    **Return** HTTP 204
234
+
235
+.. note::
236
+
237
+     If push fails and they need to start again, what happens in the index, there will already be a record for the namespace/name, but it will be initialized. Should we allow it, or mark as name already used? One edge case could be if someone pushes the same thing at the same time with two different shells.
238
+
239
+     If it's a retry on the Registry, Docker has a cookie (provided by the registry after token validation). So the Index won’t have to provide a new token.
240
+
241
+2.3 Delete
242
+----------
243
+
244
+If you need to delete something from the index or registry, we need a nice clean way to do that. Here is the workflow.
245
+
246
+1. Docker contacts the index to request a delete of a repository “samalba/busybox” (authentication required with user credentials)
247
+2. If authentication works and repository is valid, “samalba/busybox” is marked as deleted and a temporary token is returned
248
+3. Send a delete request to the registry for the repository (along with the token)
249
+4. Registry A contacts the Index to verify the token (token must corresponds to the repository name)
250
+5. Index validates the token. Registry A deletes the repository and everything associated to it.
251
+6. docker contacts the index to let it know it was removed from the registry, the index removes all records from the database.
252
+
253
+.. note::
254
+
255
+    The Docker client should present an "Are you sure?" prompt to confirm the deletion before starting the process. Once it starts it can't be undone.
256
+
257
+API (deleting repository foo/bar):
258
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
259
+
260
+1. (Docker -> Index) DELETE /v1/repositories/foo/bar/
261
+    **Headers**:
262
+        Authorization: Basic sdkjfskdjfhsdkjfh==
263
+        X-Docker-Token: true
264
+
265
+    **Action**::
266
+        - in index, we make sure it is a valid repository, and set to deleted (logically)
267
+
268
+    **Body**::
269
+        Empty
270
+
271
+2. (Index -> Docker) 202 Accepted
272
+    **Headers**:
273
+        - WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=delete
274
+        - X-Docker-Endpoints: registry.docker.io [, registry2.docker.io]   # list of endpoints where this repo lives.
275
+
276
+3. (Docker -> Registry) DELETE /v1/repositories/foo/bar/
277
+    **Headers**:
278
+        Authorization: Token signature=123abc,repository=”foo/bar”,access=delete
279
+
280
+4. (Registry->Index) PUT /v1/repositories/foo/bar/auth
281
+    **Headers**:
282
+        Authorization: Token signature=123abc,repository=”foo/bar”,access=delete
283
+    **Action**::
284
+        - Index:
285
+            will invalidate the token.
286
+        - Registry:
287
+            deletes the repository (if token is approved)
288
+
289
+5. (Registry -> Docker) 200 OK
290
+        200 If success 
291
+        403 if forbidden
292
+        400 if bad request
293
+        404 if repository isn't found
294
+
295
+6. (Docker -> Index) DELETE /v1/repositories/foo/bar/
296
+
297
+    **Headers**:
298
+        Authorization: Basic 123oislifjsldfj==
299
+        X-Docker-Endpoints: registry-1.docker.io (no validation on this right now)
300
+
301
+    **Body**:
302
+        Empty
303
+
304
+    **Return** HTTP 200
305
+
306
+
307
+3. How to use the Registry in standalone mode
308
+=============================================
309
+
310
+The Index has two main purposes (along with its fancy social features):
311
+
312
+- Resolve short names (to avoid passing absolute URLs all the time)
313
+   - username/projectname -> \https://registry.docker.io/users/<username>/repositories/<projectname>/
314
+   - team/projectname -> \https://registry.docker.io/team/<team>/repositories/<projectname>/
315
+- Authenticate a user as a repos owner (for a central referenced repository)
316
+
317
+3.1 Without an Index
318
+--------------------
319
+Using the Registry without the Index can be useful to store the images on a private network without having to rely on an external entity controlled by dotCloud.
320
+
321
+In this case, the registry will be launched in a special mode (--standalone? --no-index?). In this mode, the only thing which changes is that Registry will never contact the Index to verify a token. It will be the Registry owner responsibility to authenticate the user who pushes (or even pulls) an image using any mechanism (HTTP auth, IP based, etc...).
322
+
323
+In this scenario, the Registry is responsible for the security in case of data corruption since the checksums are not delivered by a trusted entity.
324
+
325
+As hinted previously, a standalone registry can also be implemented by any HTTP server handling GET/PUT requests (or even only GET requests if no write access is necessary).
326
+
327
+3.2 With an Index
328
+-----------------
329
+
330
+The Index data needed by the Registry are simple:
331
+- Serve the checksums
332
+- Provide and authorize a Token
333
+
334
+In the scenario of a Registry running on a private network with the need of centralizing and authorizing, it’s easy to use a custom Index.
335
+
336
+The only challenge will be to tell Docker to contact (and trust) this custom Index. Docker will be configurable at some point to use a specific Index, it’ll be the private entity responsibility (basically the organization who uses Docker in a private environment) to maintain the Index and the Docker’s configuration among its consumers.
337
+
338
+4. The API
339
+==========
340
+
341
+The first version of the api is available here: https://github.com/jpetazzo/docker/blob/acd51ecea8f5d3c02b00a08176171c59442df8b3/docs/images-repositories-push-pull.md
342
+
343
+4.1 Images
344
+----------
345
+
346
+The format returned in the images is not defined here (for layer and json), basically because Registry stores exactly the same kind of information as Docker uses to manage them.
347
+
348
+The format of ancestry is a line-separated list of image ids, in age order. I.e. the image’s parent is on the last line, the parent of the parent on the next-to-last line, etc.; if the image has no parent, the file is empty.
349
+
350
+GET /v1/images/<image_id>/layer
351
+PUT /v1/images/<image_id>/layer
352
+GET /v1/images/<image_id>/json
353
+PUT /v1/images/<image_id>/json
354
+GET /v1/images/<image_id>/ancestry
355
+PUT /v1/images/<image_id>/ancestry
356
+
357
+4.2 Users
358
+---------
359
+
360
+4.2.1 Create a user (Index)
361
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
362
+
363
+POST /v1/users
364
+
365
+**Body**:
366
+    {"email": "sam@dotcloud.com", "password": "toto42", "username": "foobar"'}
367
+
368
+**Validation**:
369
+    - **username** : min 4 character, max 30 characters, must match the regular expression [a-z0-9_].
370
+    - **password**: min 5 characters
371
+
372
+**Valid**: return HTTP 200
373
+
374
+Errors: HTTP 400 (we should create error codes for possible errors)
375
+- invalid json
376
+- missing field
377
+- wrong format (username, password, email, etc)
378
+- forbidden name
379
+- name already exists
380
+
381
+.. note::
382
+
383
+    A user account will be valid only if the email has been validated (a validation link is sent to the email address).
384
+
385
+4.2.2 Update a user (Index)
386
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
387
+
388
+PUT /v1/users/<username>
389
+
390
+**Body**:
391
+    {"password": "toto"}
392
+
393
+.. note::
394
+
395
+    We can also update email address, if they do, they will need to reverify their new email address.
396
+
397
+4.2.3 Login (Index)
398
+^^^^^^^^^^^^^^^^^^^
399
+Does nothing else but asking for a user authentication. Can be used to validate credentials. HTTP Basic Auth for now, maybe change in future.
400
+
401
+GET /v1/users
402
+
403
+**Return**:
404
+    - Valid: HTTP 200
405
+    - Invalid login: HTTP 401
406
+    - Account inactive: HTTP 403 Account is not Active
407
+
408
+4.3 Tags (Registry)
409
+-------------------
410
+
411
+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.
412
+
413
+The following naming restrictions apply:
414
+
415
+- Namespaces must match the same regular expression as usernames (See 4.2.1.)
416
+- Repository names must match the regular expression [a-zA-Z0-9-_.]
417
+
418
+4.3.1 Get all tags
419
+^^^^^^^^^^^^^^^^^^
420
+
421
+GET /v1/repositories/<namespace>/<repository_name>/tags
422
+
423
+**Return**: HTTP 200
424
+    {
425
+    "latest": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
426
+    “0.1.1”:  “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
427
+    }
428
+
429
+4.3.2 Read the content of a tag (resolve the image id)
430
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
431
+
432
+GET /v1/repositories/<namespace>/<repo_name>/tags/<tag>
433
+
434
+**Return**:
435
+    "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
436
+
437
+4.3.3 Delete a tag (registry)
438
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
439
+
440
+DELETE /v1/repositories/<namespace>/<repo_name>/tags/<tag>
441
+
442
+4.4 Images (Index)
443
+------------------
444
+
445
+For the Index to “resolve” the repository name to a Registry location, it uses the X-Docker-Endpoints header. In other terms, this requests always add a “X-Docker-Endpoints” to indicate the location of the registry which hosts this repository.
446
+
447
+4.4.1 Get the images
448
+^^^^^^^^^^^^^^^^^^^^^
449
+
450
+GET /v1/repositories/<namespace>/<repo_name>/images
451
+
452
+**Return**: HTTP 200
453
+    [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”, “checksum”: “md5:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
454
+
455
+
456
+4.4.2 Add/update the images
457
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
458
+
459
+You always add images, you never remove them.
460
+
461
+PUT /v1/repositories/<namespace>/<repo_name>/images
462
+
463
+**Body**:
464
+    [ {“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”, “checksum”: “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”} ]
465
+
466
+**Return** 204
467
+
468
+4.5 Repositories
469
+----------------
470
+
471
+4.5.1 Remove a Repository (Registry)
472
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
473
+
474
+DELETE /v1/repositories/<namespace>/<repo_name>
475
+
476
+Return 200 OK
477
+
478
+4.5.2 Remove a Repository (Index)
479
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
480
+This starts the delete process. see 2.3 for more details.
481
+
482
+DELETE /v1/repositories/<namespace>/<repo_name>
483
+
484
+Return 202 OK
485
+
486
+5. Chaining Registries
487
+======================
488
+
489
+It’s possible to chain Registries server for several reasons:
490
+- Load balancing
491
+- Delegate the next request to another server
492
+
493
+When a Registry is a reference for a repository, it should host the entire images chain in order to avoid breaking the chain during the download.
494
+
495
+The Index and Registry use this mechanism to redirect on one or the other.
496
+
497
+Example with an image download:
498
+On every request, a special header can be returned:
499
+
500
+X-Docker-Endpoints: server1,server2
501
+
502
+On the next request, the client will always pick a server from this list.
503
+
504
+6. Authentication & Authorization
505
+=================================
506
+
507
+6.1 On the Index
508
+-----------------
509
+
510
+The Index supports both “Basic” and “Token” challenges. Usually when there is a “401 Unauthorized”, the Index replies this::
511
+
512
+    401 Unauthorized
513
+    WWW-Authenticate: Basic realm="auth required",Token
514
+
515
+You have 3 options:
516
+
517
+1. Provide user credentials and ask for a token
518
+
519
+    **Header**:
520
+        - Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
521
+        - X-Docker-Token: true
522
+
523
+    In this case, along with the 200 response, you’ll get a new token (if user auth is ok):
524
+    If authorization isn't correct you get a 401 response.
525
+    If account isn't active you will get a 403 response.
526
+
527
+    **Response**:
528
+        - 200 OK
529
+        - X-Docker-Token: Token signature=123abc,repository=”foo/bar”,access=read
530
+
531
+2. Provide user credentials only
532
+
533
+    **Header**:
534
+        Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
535
+
536
+3. Provide Token
537
+
538
+    **Header**:
539
+        Authorization: Token signature=123abc,repository=”foo/bar”,access=read
540
+
541
+6.2 On the Registry
542
+-------------------
543
+
544
+The Registry only supports the Token challenge::
545
+
546
+    401 Unauthorized
547
+    WWW-Authenticate: Token
548
+
549
+The only way is to provide a token on “401 Unauthorized” responses::
550
+
551
+    Authorization: Token signature=123abc,repository=”foo/bar”,access=read
552
+
553
+Usually, the Registry provides a Cookie when a Token verification succeeded. Every time the Registry passes a Cookie, you have to pass it back the same cookie.::
554
+
555
+    200 OK
556
+    Set-Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=&timestamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="; Path=/; HttpOnly
557
+
558
+Next request::
559
+
560
+    GET /(...)
561
+    Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=&timestamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="
562
+
563
+
564
+7.0 Document Version
565
+---------------------
566
+
567
+- 1.0 : May 6th 2013 : initial release 
568
+- 1.1 : June 1st 2013 : Added Delete Repository and way to handle new source namespace.
0 569
\ No newline at end of file
... ...
@@ -19,10 +19,15 @@ Examples
19 19
 
20 20
     docker build .
21 21
 
22
-This will take the local Dockerfile
22
+| This will read the Dockerfile from the current directory. It will also send any other files and directories found in the current directory to the docker daemon.
23
+| The contents of this directory would be used by ADD commands found within the Dockerfile.
24
+| This will send a lot of data to the docker daemon if the current directory contains a lot of data.
25
+| If the absolute path is provided instead of '.', only the files and directories required by the ADD commands from the Dockerfile will be added to the context and transferred to the docker daemon.
26
+|
23 27
 
24 28
 .. code-block:: bash
25 29
 
26 30
     docker build -
27 31
 
28
-This will read a Dockerfile form Stdin without context
32
+| This will read a Dockerfile from Stdin without context. Due to the lack of a context, no contents of any local directory will be sent to the docker daemon.
33
+| ADD doesn't work when running in this mode due to the absence of the context, thus having no source files to copy to the container.
... ...
@@ -5,5 +5,5 @@
5 5
 Contributing to Docker
6 6
 ======================
7 7
 
8
-Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started <https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`.
8
+Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started <https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`_.
9 9
 
... ...
@@ -5,11 +5,35 @@
5 5
 Setting Up a Dev Environment
6 6
 ============================
7 7
 
8
-Instructions that have been verified to work on Ubuntu 12.10,
8
+Instructions that have been verified to work on Ubuntu Precise 12.04 (LTS) (64-bit),
9
+
10
+
11
+Dependencies
12
+------------
13
+
14
+**Linux kernel 3.8**
15
+
16
+Due to a bug in LXC docker works best on the 3.8 kernel. Precise comes with a 3.2 kernel, so we need to upgrade it. The kernel we install comes with AUFS built in.
17
+
9 18
 
10 19
 .. code-block:: bash
11 20
 
12
-    sudo apt-get -y install lxc wget bsdtar curl golang git
21
+   # install the backported kernel
22
+   sudo apt-get update && sudo apt-get install linux-image-generic-lts-raring
23
+
24
+   # reboot
25
+   sudo reboot
26
+
27
+
28
+Installation
29
+------------
30
+
31
+.. code-block:: bash
32
+		
33
+    sudo apt-get install python-software-properties
34
+    sudo add-apt-repository ppa:gophers/go
35
+    sudo apt-get update
36
+    sudo apt-get -y install lxc wget bsdtar curl golang-stable git
13 37
 
14 38
     export GOPATH=~/go/
15 39
     export PATH=$GOPATH/bin:$PATH
... ...
@@ -19,7 +19,8 @@ Most frequently asked questions.
19 19
 
20 20
 3. **Does Docker run on Mac OS X or Windows?**
21 21
 
22
-   Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a virtual machine on your box, and get the best of both worlds. Check out the MacOSX_ and Windows_ installation guides.
22
+   Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a
23
+   virtual machine on your box, and get the best of both worlds. Check out the :ref:`install_using_vagrant` and :ref:`windows` installation guides.
23 24
 
24 25
 4. **How do containers compare to virtual machines?**
25 26
 
... ...
@@ -34,15 +35,16 @@ Most frequently asked questions.
34 34
 
35 35
     You can find more answers on:
36 36
 
37
-    * `IRC: docker on freenode`_
37
+    * `Docker club mailinglist`_
38
+    * `IRC, docker on freenode`_
38 39
     * `Github`_
39 40
     * `Ask questions on Stackoverflow`_
40 41
     * `Join the conversation on Twitter`_
41 42
 
42
-    .. _Windows: ../installation/windows/
43
-    .. _MacOSX: ../installation/vagrant/
43
+
44
+    .. _Docker club mailinglist: https://groups.google.com/d/forum/docker-club
44 45
     .. _the repo: http://www.github.com/dotcloud/docker
45
-    .. _IRC\: docker on freenode: irc://chat.freenode.net#docker
46
+    .. _IRC, docker on freenode: irc://chat.freenode.net#docker
46 47
     .. _Github: http://www.github.com/dotcloud/docker
47 48
     .. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker
48 49
     .. _Join the conversation on Twitter: http://twitter.com/getdocker
... ...
@@ -92,6 +92,16 @@ have AUFS filesystem support enabled, so we need to install it.
92 92
    sudo apt-get update
93 93
    sudo apt-get install linux-image-extra-`uname -r`
94 94
 
95
+**add-apt-repository support**
96
+
97
+Some installations of Ubuntu 13.04 require ``software-properties-common`` to be
98
+installed before being able to use add-apt-repository.
99
+
100
+.. code-block:: bash
101
+
102
+  sudo apt-get install software-properties-common
103
+
104
+
95 105
 Installation
96 106
 ------------
97 107
 
... ...
@@ -2,6 +2,7 @@
2 2
 :description: Docker's tutorial to run docker on Windows
3 3
 :keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
4 4
 
5
+.. _windows:
5 6
 
6 7
 Using Vagrant (Windows)
7 8
 =======================
... ...
@@ -18,7 +18,7 @@ steps and commit them along the way, giving you a final image.
18 18
 To use Docker Builder, assemble the steps into a text file (commonly referred to
19 19
 as a Dockerfile) and supply this to `docker build` on STDIN, like so:
20 20
 
21
-    ``docker build < Dockerfile``
21
+    ``docker build - < Dockerfile``
22 22
 
23 23
 Docker will run your steps one-by-one, committing the result if necessary, 
24 24
 before finally outputting the ID of your new image.
... ...
@@ -64,14 +64,15 @@
64 64
 
65 65
             <div style="float: right" class="pull-right">
66 66
                 <ul class="nav">
67
-                    <li><a href="http://www.docker.io/">Introduction</a></li>
68
-                    <li><a href="http://www.docker.io/gettingstarted/">Getting started</a></li>
69
-                    <li class="active"><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
67
+                    <li id="nav-introduction"><a href="http://www.docker.io/">Introduction</a></li>
68
+                    <li id="nav-gettingstarted"><a href="http://www.docker.io/gettingstarted/">Getting started</a></li>
69
+                    <li id="nav-documentation" class="active"><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
70
+                    <li id="nav-blog"><a href="http://blog.docker.io/">Blog</a></li>
70 71
                 </ul>
71
-                <div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
72
-                    <a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
73
-                    <a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
74
-                </div>
72
+                <!--<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">-->
73
+                    <!--<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>-->
74
+                    <!--<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>-->
75
+                <!--</div>-->
75 76
             </div>
76 77
 
77 78
             <div style="margin-left: -12px; float: left;">
... ...
@@ -86,8 +87,13 @@
86 86
 
87 87
 <div class="container">
88 88
     <div class="row">
89
-        <div class="span12 titlebar"><h1 class="pageheader">DOCUMENTATION</h1>
89
+        <div class="span12 titlebar">
90 90
             <!--<span class="pull-right" style="margin-left: 20px; font-size: 20px">{{version}}</span>-->
91
+            <div class="pull-right" id="fork-us" style="margin-top: 16px; margin-right: 16px;">
92
+                <a  href="http://github.com/dotcloud/docker/"><img src="{{ pathto('_static/img/fork-us.png', 1) }}"> Fork us on Github</a>
93
+            </div>
94
+            <h1 class="pageheader">DOCUMENTATION</h1>
95
+
91 96
         </div>
92 97
     </div>
93 98
 
... ...
@@ -123,8 +129,14 @@
123 123
     <div class="row">
124 124
 
125 125
         <div class="span12 footer">
126
+            <div class="tbox textright forceleftmargin social links pull-right">
127
+                <a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
128
+                <a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
129
+            </div>
126 130
 
127 131
             Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
132
+
133
+
128 134
 {#            {%- if show_source and has_source and sourcename %}#}
129 135
 {#            ·#}
130 136
 {#            <a href="{{ pathto('_sources/' + sourcename, true)|e }}"#}
... ...
@@ -157,7 +169,7 @@
157 157
   <!-- script which should be loaded after everything else -->
158 158
 <script type="text/javascript">
159 159
 
160
-
160
+    // Function to make the sticky header possible
161 161
     var shiftWindow = function() {
162 162
         scrollBy(0, -70);
163 163
         console.log("window shifted")
... ...
@@ -285,6 +285,40 @@ section.header {
285 285
 .social .github {
286 286
   background-position: -59px 2px;
287 287
 }
288
+#fork-us {
289
+  /*font-family: 'Maven Pro';*/
290
+
291
+  /*font-weight: bold;*/
292
+
293
+  font-size: 12px;
294
+  /*text-transform: uppercase;*/
295
+
296
+  display: block;
297
+  padding: 0px 1em;
298
+  height: 28px;
299
+  line-height: 28px;
300
+  background-color: #43484c;
301
+  filter: progid:dximagetransform.microsoft.gradient(gradientType=0, startColorstr='#FFFF6E56', endColorstr='#FFED4F35');
302
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #747474), color-stop(100%, #43484c));
303
+  background-image: -webkit-linear-gradient(top, #747474 0%, #43484c 100%);
304
+  background-image: -moz-linear-gradient(top, #747474 0%, #43484c 100%);
305
+  background-image: -o-linear-gradient(top, #747474 0%, #43484c 100%);
306
+  background-image: linear-gradient(top, #747474 0%, #43484c 100%);
307
+  border: 1px solid #43484c;
308
+  -webkit-border-radius: 4px;
309
+  -moz-border-radius: 4px;
310
+  -ms-border-radius: 4px;
311
+  -o-border-radius: 4px;
312
+  border-radius: 4px;
313
+  -webkit-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
314
+  -moz-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
315
+  box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
316
+  margin: 8px;
317
+}
318
+#fork-us a {
319
+  color: #faf2ee;
320
+  text-shadow: rgba(0, 0, 0, 0.3) 0px 1px 0px;
321
+}
288 322
 /* =======================
289 323
    Media size overrides
290 324
 ======================= */
... ...
@@ -325,10 +359,15 @@ section.header {
325 325
   
326 326
     padding-top: 600px;
327 327
   }
328
+  #fork-us {
329
+    display: none;
330
+  }
328 331
 }
329 332
 /* Landscape phones and down */
330 333
 @media (max-width: 480px) {
331
-  
334
+  #nav-gettingstarted {
335
+    display: none;
336
+  }
332 337
 }
333 338
 /* Misc fixes */
334 339
 table th {
... ...
@@ -391,6 +391,38 @@ section.header {
391 391
 }
392 392
 
393 393
 
394
+#fork-us {
395
+  /*font-family: 'Maven Pro';*/
396
+  /*font-weight: bold;*/
397
+  font-size: 12px;
398
+  /*text-transform: uppercase;*/
399
+  display: block;
400
+  padding: 0px 1em;
401
+  height: 28px;
402
+  line-height: 28px;
403
+  background-color: #43484c;
404
+  filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFFF6E56', endColorstr='#FFED4F35');
405
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #747474), color-stop(100%, #43484c));
406
+  background-image: -webkit-linear-gradient(top, #747474 0%, #43484c 100%);
407
+  background-image: -moz-linear-gradient(top, #747474 0%, #43484c 100%);
408
+  background-image: -o-linear-gradient(top, #747474 0%, #43484c 100%);
409
+  background-image: linear-gradient(top, #747474 0%, #43484c 100%);
410
+  border: 1px solid #43484c;
411
+  -webkit-border-radius: 4px;
412
+  -moz-border-radius: 4px;
413
+  -ms-border-radius: 4px;
414
+  -o-border-radius: 4px;
415
+  border-radius: 4px;
416
+  -webkit-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
417
+  -moz-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
418
+  box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
419
+  margin: 8px;
420
+
421
+  a {
422
+    color: #faf2ee;
423
+    text-shadow: rgba(0, 0, 0, 0.3) 0px 1px 0px;
424
+  }
425
+}
394 426
 /* =======================
395 427
    Media size overrides
396 428
 ======================= */
... ...
@@ -441,14 +473,17 @@ section.header {
441 441
   /* TODO: Fix this to be relative to the navigation size */
442 442
     padding-top: 600px;
443 443
   }
444
-
444
+  #fork-us {
445
+    display: none;
446
+  }
445 447
 }
446 448
 
447 449
 
448 450
 /* Landscape phones and down */
449 451
 @media (max-width: 480px) {
450
-
451
-
452
+  #nav-gettingstarted {
453
+    display: none;
454
+  }
452 455
 }
453 456
 
454 457
 /* Misc fixes */
... ...
@@ -34,15 +34,11 @@
34 34
 
35 35
             <div style="float: right" class="pull-right">
36 36
                 <ul class="nav">
37
-                    <li><a href="../">Introduction</a></li>
38
-                    <li class="active"><a href="">Getting started</a></li>
39
-                    <li class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
37
+                    <li id="nav-introduction"><a href="../">Introduction</a></li>
38
+                    <li id="nav-gettingstarted" class="active"><a href="">Getting started</a></li>
39
+                    <li id="nav-documentation" class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
40
+                    <li id="nav-blog"><a href="http://blog.docker.io/">Blog</a></li>
40 41
                 </ul>
41
-
42
-                <div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
43
-                    <a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
44
-                    <a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
45
-                </div>
46 42
             </div>
47 43
 
48 44
             <div style="margin-left: -12px; float: left;">
... ...
@@ -55,14 +51,22 @@
55 55
 
56 56
 <div class="container">
57 57
     <div class="row">
58
-        <div class="span12 titlebar"><h1 class="pageheader">GETTING STARTED</h1>
58
+
59
+        <div class="span12 titlebar">
60
+
61
+            <div class="pull-right" id="fork-us" style="margin-top: 16px; margin-right: 16px;">
62
+                <a  href="http://github.com/dotcloud/docker/"><img src="../static/img/fork-us.png"> Fork us on Github</a>
63
+            </div>
64
+
65
+            <h1 class="pageheader"> GETTING STARTED</h1>
59 66
         </div>
67
+
60 68
     </div>
61 69
 
62 70
 </div>
63 71
 
64 72
 <div class="container">
65
-    <div class="alert alert-info">
73
+    <div class="alert alert-info" style="margin-bottom: 0;">
66 74
         <strong>Docker is still under heavy development.</strong> It should not yet be used in production. Check <a href="http://github.com/dotcloud/docker">the repo</a> for recent progress.
67 75
     </div>
68 76
     <div class="row">
... ...
@@ -133,13 +137,13 @@
133 133
             </section>
134 134
 
135 135
             <section class="contentblock">
136
-                <h2>More resources</h2>
137
-                <ul>
138
-                    <li><a href="irc://chat.freenode.net#docker">IRC: docker on freenode</a></li>
139
-                    <li><a href="http://www.github.com/dotcloud/docker">Github</a></li>
140
-                    <li><a href="http://stackoverflow.com/tags/docker/">Ask questions on Stackoverflow</a></li>
141
-                    <li><a href="http://twitter.com/getdocker/">Join the conversation on Twitter</a></li>
142
-                </ul>
136
+                <h2>Questions? Want to get in touch?</h2>
137
+                <p>There are several ways to get in touch:</p>
138
+                <p><strong>Join the discussion on IRC.</strong> We can be found in the <a href="irc://chat.freenode.net#docker">#docker</a> channel on chat.freenode.net</p>
139
+                <p><strong>Discussions</strong> happen on our google group: <a href="https://groups.google.com/d/forum/docker-club">docker-club at googlegroups.com</a></p>
140
+                <p>All our <strong>development and decisions</strong> are made out in the open on Github <a href="http://www.github.com/dotcloud/docker">github.com/dotcloud/docker</a></p>
141
+                <p><strong>Get help on using Docker</strong> by asking on <a href="http://stackoverflow.com/tags/docker/">Stackoverflow</a></p>
142
+                <p>And of course, <strong>tweet</strong> your tweets to <a href="http://twitter.com/getdocker/">twitter.com/getdocker</a></p>
143 143
             </section>
144 144
 
145 145
 
... ...
@@ -172,7 +176,10 @@
172 172
     <footer id="footer" class="footer">
173 173
         <div class="row">
174 174
             <div class="span12 social">
175
-
175
+                <div class="tbox textright forceleftmargin social links pull-right">
176
+                    <a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
177
+                    <a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
178
+                </div>
176 179
                 Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
177 180
 
178 181
             </div>
... ...
@@ -44,9 +44,18 @@
44 44
         .debug {
45 45
             border: 1px red dotted;
46 46
         }
47
+        .twitterblock {
48
+            min-height: 75px;
49
+        }
50
+
51
+        .twitterblock img {
52
+            float: left;
53
+            margin-right: 10px;
54
+        }
47 55
 
48 56
     </style>
49 57
 
58
+
50 59
 </head>
51 60
 
52 61
 
... ...
@@ -56,17 +65,18 @@
56 56
     <div class="navbar-dotcloud">
57 57
         <div class="container" style="text-align: center;">
58 58
 
59
+
60
+            <div class="pull-left" id="fork-us" style="margin-top: 16px;">
61
+                <a  href="http://github.com/dotcloud/docker/"><img src="static/img/fork-us.png" alt="fork-icon"> Fork us on Github</a>
62
+            </div>
63
+
59 64
             <div class="pull-right" >
60 65
                 <ul class="nav">
61
-                    <li class="active"><a href="/">Introduction</a></li>
62
-                    <li ><a href="gettingstarted">Getting started</a></li>
63
-                    <li class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
66
+                    <li id="nav-introduction" class="active"><a href="/">Introduction</a></li>
67
+                    <li id="nav-gettingstarted"><a href="gettingstarted">Getting started</a></li>
68
+                    <li id="nav-documentation" class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
69
+                    <li id="nav-blog"><a href="http://blog.docker.io/">Blog</a></li>
64 70
                 </ul>
65
-
66
-                <div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
67
-                    <a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
68
-                    <a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
69
-                </div>
70 71
             </div>
71 72
         </div>
72 73
     </div>
... ...
@@ -81,7 +91,7 @@
81 81
 
82 82
                 <div class="span5" style="margin-bottom: 15px;">
83 83
                     <div style="text-align: center;" >
84
-                        <img src="static/img/docker_letters_500px.png">
84
+                        <img src="static/img/docker_letters_500px.png" alt="docker letters">
85 85
 
86 86
                         <h2>The Linux container engine</h2>
87 87
                     </div>
... ...
@@ -130,7 +140,7 @@
130 130
             <section class="contentblock">
131 131
                 <div class="container">
132 132
                 <div class="span2" style="margin-left: 0" >
133
-                    <a href="http://dotcloud.theresumator.com/apply/mWjkD4/Software-Engineer.html" title="Job description"><img src="static/img/hiring_graphic.png" width="140px" style="margin-top: 25px"></a>
133
+                    <a href="http://dotcloud.theresumator.com/apply/mWjkD4/Software-Engineer.html" title="Job description"><img src="static/img/hiring_graphic.png" alt="we're hiring" width="140" style="margin-top: 25px"></a>
134 134
                 </div>
135 135
                 <div class="span4" style="margin-left: 0">
136 136
                     <h4>Do you think it is cool to hack on docker? Join us!</h4>
... ...
@@ -156,7 +166,7 @@
156 156
                     </div>
157 157
                 </a>
158 158
                 &nbsp;
159
-                <input type="button" class="searchbutton" type="submit" value="Search images"
159
+                <input type="button" class="searchbutton" value="Search images"
160 160
                        onClick="window.open('https://index.docker.io')" />
161 161
 
162 162
             </section>
... ...
@@ -184,32 +194,19 @@
184 184
 
185 185
 </div>
186 186
 
187
-<style>
188
-    .twitterblock {
189
-        min-height: 75px;
190
-    }
191
-
192
-    .twitterblock img {
193
-        float: left;
194
-        margin-right: 10px;
195
-    }
196
-
197
-</style>
198
-
199
-
200 187
 <div class="container">
201 188
 
202 189
     <div class="row">
203 190
         <div class="span6">
204 191
             <section class="contentblock twitterblock">
205 192
                 <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!!
193
+                <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 194
             </section>
208 195
         </div>
209 196
         <div class="span6">
210 197
             <section class="contentblock twitterblock">
211 198
                 <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.
199
+                <em>Adam Jacob ‏@adamhjk:</em> Docker is clearly the right idea. @solomonstre absolutely killed it. Containerized app deployment is the future, I think.
213 200
             </section>
214 201
         </div>
215 202
     </div>
... ...
@@ -217,13 +214,13 @@
217 217
         <div class="span6">
218 218
             <section class="contentblock twitterblock">
219 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.
220
+                <em>Matt Townsend ‏@mtownsend:</em> I have a serious code crush on docker.io - it's Lego for PaaS. Motherfucking awesome Lego.
221 221
             </section>
222 222
         </div>
223 223
         <div class="span6">
224 224
             <section class="contentblock twitterblock">
225 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.
226
+                <em>Rob Harrop ‏@robertharrop:</em> Impressed by @getdocker - it's all kinds of magic. Serious rethink of AWS architecture happening @skillsmatter.
227 227
             </section>
228 228
         </div>
229 229
     </div>
... ...
@@ -317,7 +314,10 @@
317 317
     <footer id="footer" class="footer">
318 318
         <div class="row">
319 319
             <div class="span12">
320
-
320
+                <div class="tbox textright forceleftmargin social links pull-right">
321
+                    <a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
322
+                    <a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
323
+                </div>
321 324
                 Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
322 325
 
323 326
             </div>
... ...
@@ -86,14 +86,14 @@ func (graph *Graph) Get(name string) (*Image, error) {
86 86
 	if err != nil {
87 87
 		return nil, err
88 88
 	}
89
-	if img.Id != id {
90
-		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.Id)
89
+	if img.ID != id {
90
+		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
91 91
 	}
92 92
 	img.graph = graph
93 93
 	graph.lockSumMap.Lock()
94 94
 	defer graph.lockSumMap.Unlock()
95
-	if _, exists := graph.checksumLock[img.Id]; !exists {
96
-		graph.checksumLock[img.Id] = &sync.Mutex{}
95
+	if _, exists := graph.checksumLock[img.ID]; !exists {
96
+		graph.checksumLock[img.ID] = &sync.Mutex{}
97 97
 	}
98 98
 	return img, nil
99 99
 }
... ...
@@ -101,7 +101,7 @@ func (graph *Graph) Get(name string) (*Image, error) {
101 101
 // Create creates a new image and registers it in the graph.
102 102
 func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
103 103
 	img := &Image{
104
-		Id:            GenerateId(),
104
+		ID:            GenerateID(),
105 105
 		Comment:       comment,
106 106
 		Created:       time.Now(),
107 107
 		DockerVersion: VERSION,
... ...
@@ -111,7 +111,7 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
111 111
 	}
112 112
 	if container != nil {
113 113
 		img.Parent = container.Image
114
-		img.Container = container.Id
114
+		img.Container = container.ID
115 115
 		img.ContainerConfig = *container.Config
116 116
 	}
117 117
 	if err := graph.Register(layerData, layerData != nil, img); err != nil {
... ...
@@ -124,12 +124,12 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
124 124
 // Register imports a pre-existing image into the graph.
125 125
 // FIXME: pass img as first argument
126 126
 func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
127
-	if err := ValidateId(img.Id); err != nil {
127
+	if err := ValidateID(img.ID); err != nil {
128 128
 		return err
129 129
 	}
130 130
 	// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
131
-	if graph.Exists(img.Id) {
132
-		return fmt.Errorf("Image %s already exists", img.Id)
131
+	if graph.Exists(img.ID) {
132
+		return fmt.Errorf("Image %s already exists", img.ID)
133 133
 	}
134 134
 	tmp, err := graph.Mktemp("")
135 135
 	defer os.RemoveAll(tmp)
... ...
@@ -140,12 +140,12 @@ func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
140 140
 		return err
141 141
 	}
142 142
 	// Commit
143
-	if err := os.Rename(tmp, graph.imageRoot(img.Id)); err != nil {
143
+	if err := os.Rename(tmp, graph.imageRoot(img.ID)); err != nil {
144 144
 		return err
145 145
 	}
146 146
 	img.graph = graph
147
-	graph.idIndex.Add(img.Id)
148
-	graph.checksumLock[img.Id] = &sync.Mutex{}
147
+	graph.idIndex.Add(img.ID)
148
+	graph.checksumLock[img.ID] = &sync.Mutex{}
149 149
 	return nil
150 150
 }
151 151
 
... ...
@@ -173,7 +173,7 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output
173 173
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.
174 174
 func (graph *Graph) Mktemp(id string) (string, error) {
175 175
 	if id == "" {
176
-		id = GenerateId()
176
+		id = GenerateID()
177 177
 	}
178 178
 	tmp, err := graph.tmp()
179 179
 	if err != nil {
... ...
@@ -230,7 +230,7 @@ func (graph *Graph) Map() (map[string]*Image, error) {
230 230
 	}
231 231
 	images := make(map[string]*Image, len(all))
232 232
 	for _, image := range all {
233
-		images[image.Id] = image
233
+		images[image.ID] = image
234 234
 	}
235 235
 	return images, nil
236 236
 }
... ...
@@ -273,10 +273,10 @@ func (graph *Graph) ByParent() (map[string][]*Image, error) {
273 273
 		if err != nil {
274 274
 			return
275 275
 		}
276
-		if children, exists := byParent[parent.Id]; exists {
277
-			byParent[parent.Id] = []*Image{image}
276
+		if children, exists := byParent[parent.ID]; exists {
277
+			byParent[parent.ID] = []*Image{image}
278 278
 		} else {
279
-			byParent[parent.Id] = append(children, image)
279
+			byParent[parent.ID] = append(children, image)
280 280
 		}
281 281
 	})
282 282
 	return byParent, err
... ...
@@ -293,8 +293,8 @@ func (graph *Graph) Heads() (map[string]*Image, error) {
293 293
 	err = graph.WalkAll(func(image *Image) {
294 294
 		// If it's not in the byParent lookup table, then
295 295
 		// it's not a parent -> so it's a head!
296
-		if _, exists := byParent[image.Id]; !exists {
297
-			heads[image.Id] = image
296
+		if _, exists := byParent[image.ID]; !exists {
297
+			heads[image.ID] = image
298 298
 		}
299 299
 	})
300 300
 	return heads, err
... ...
@@ -317,11 +317,11 @@ func (graph *Graph) getStoredChecksums() (map[string]string, error) {
317 317
 }
318 318
 
319 319
 func (graph *Graph) storeChecksums(checksums map[string]string) error {
320
-	checksumJson, err := json.Marshal(checksums)
320
+	checksumJSON, err := json.Marshal(checksums)
321 321
 	if err != nil {
322 322
 		return err
323 323
 	}
324
-	if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJson, 0600); err != nil {
324
+	if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJSON, 0600); err != nil {
325 325
 		return err
326 326
 	}
327 327
 	return nil
... ...
@@ -34,14 +34,14 @@ func TestInterruptedRegister(t *testing.T) {
34 34
 	defer os.RemoveAll(graph.Root)
35 35
 	badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
36 36
 	image := &Image{
37
-		Id:      GenerateId(),
37
+		ID:      GenerateID(),
38 38
 		Comment: "testing",
39 39
 		Created: time.Now(),
40 40
 	}
41 41
 	go graph.Register(badArchive, false, image)
42 42
 	time.Sleep(200 * time.Millisecond)
43 43
 	w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling)
44
-	if _, err := graph.Get(image.Id); err == nil {
44
+	if _, err := graph.Get(image.ID); err == nil {
45 45
 		t.Fatal("Image should not exist after Register is interrupted")
46 46
 	}
47 47
 	// Registering the same image again should succeed if the first register was interrupted
... ...
@@ -67,7 +67,7 @@ func TestGraphCreate(t *testing.T) {
67 67
 	if err != nil {
68 68
 		t.Fatal(err)
69 69
 	}
70
-	if err := ValidateId(image.Id); err != nil {
70
+	if err := ValidateID(image.ID); err != nil {
71 71
 		t.Fatal(err)
72 72
 	}
73 73
 	if image.Comment != "Testing" {
... ...
@@ -91,7 +91,7 @@ func TestRegister(t *testing.T) {
91 91
 		t.Fatal(err)
92 92
 	}
93 93
 	image := &Image{
94
-		Id:      GenerateId(),
94
+		ID:      GenerateID(),
95 95
 		Comment: "testing",
96 96
 		Created: time.Now(),
97 97
 	}
... ...
@@ -104,11 +104,11 @@ func TestRegister(t *testing.T) {
104 104
 	} else if l := len(images); l != 1 {
105 105
 		t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
106 106
 	}
107
-	if resultImg, err := graph.Get(image.Id); err != nil {
107
+	if resultImg, err := graph.Get(image.ID); err != nil {
108 108
 		t.Fatal(err)
109 109
 	} else {
110
-		if resultImg.Id != image.Id {
111
-			t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.Id, resultImg.Id)
110
+		if resultImg.ID != image.ID {
111
+			t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.ID, resultImg.ID)
112 112
 		}
113 113
 		if resultImg.Comment != image.Comment {
114 114
 			t.Fatalf("Wrong image comment. Should be '%s', not '%s'", image.Comment, resultImg.Comment)
... ...
@@ -156,7 +156,7 @@ func TestDeletePrefix(t *testing.T) {
156 156
 	graph := tempGraph(t)
157 157
 	defer os.RemoveAll(graph.Root)
158 158
 	img := createTestImage(graph, t)
159
-	if err := graph.Delete(utils.TruncateId(img.Id)); err != nil {
159
+	if err := graph.Delete(utils.TruncateID(img.ID)); err != nil {
160 160
 		t.Fatal(err)
161 161
 	}
162 162
 	assertNImages(graph, t, 0)
... ...
@@ -187,7 +187,7 @@ func TestDelete(t *testing.T) {
187 187
 		t.Fatal(err)
188 188
 	}
189 189
 	assertNImages(graph, t, 1)
190
-	if err := graph.Delete(img.Id); err != nil {
190
+	if err := graph.Delete(img.ID); err != nil {
191 191
 		t.Fatal(err)
192 192
 	}
193 193
 	assertNImages(graph, t, 0)
... ...
@@ -201,7 +201,7 @@ func TestDelete(t *testing.T) {
201 201
 		t.Fatal(err)
202 202
 	}
203 203
 	assertNImages(graph, t, 2)
204
-	if err := graph.Delete(img1.Id); err != nil {
204
+	if err := graph.Delete(img1.ID); err != nil {
205 205
 		t.Fatal(err)
206 206
 	}
207 207
 	assertNImages(graph, t, 1)
... ...
@@ -216,7 +216,7 @@ func TestDelete(t *testing.T) {
216 216
 	if err := graph.Register(archive, false, img1); err != nil {
217 217
 		t.Fatal(err)
218 218
 	}
219
-	if err := graph.Delete(img1.Id); err != nil {
219
+	if err := graph.Delete(img1.ID); err != nil {
220 220
 		t.Fatal(err)
221 221
 	}
222 222
 	assertNImages(graph, t, 1)
223 223
new file mode 100644
... ...
@@ -0,0 +1,19 @@
0
+# Docker principles
1
+
2
+In the design and development of Docker we try to follow these principles:
3
+
4
+(Work in progress)
5
+
6
+* Don't try to replace every tool. Instead, be an ingredient to improve them.
7
+* Less code is better.
8
+* Less components is better. Do you really need to add one more class?
9
+* 50 lines of straightforward, readable code is better than 10 lines of magic that nobody can understand.
10
+* Don't do later what you can do now. "//FIXME: refactor" is not acceptable in new code.
11
+* When hesitating between 2 options, choose the one that is easier to reverse.
12
+* No is temporary, Yes is forever. If you're not sure about a new feature, say no. You can change your mind later.
13
+* Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable.
14
+* The less moving parts in a container, the better.
15
+* Don't merge it unless you document it.
16
+* Don't document it unless you can keep it up-to-date.
17
+* Don't merge it unless you test it!
18
+* Everyone's problem is slightly different. Focus on the part that is the same for everyone, and solve that.
0 19
new file mode 100644
... ...
@@ -0,0 +1,105 @@
0
+# Docker: what's next?
1
+
2
+This document is a high-level overview of where we want to take Docker next.
3
+It is a curated selection of planned improvements which are either important, difficult, or both.
4
+
5
+For a more complete view of planned and requested improvements, see [the Github issues](https://github.com/dotcloud/docker/issues).
6
+
7
+Tu suggest changes to the roadmap, including additions, please write the change as if it were already in effect, and make a pull request.
8
+
9
+Broader kernel support
10
+----------------------
11
+
12
+Our goal is to make Docker run everywhere, but currently Docker requires [Linux version 3.8 or higher with lxc and aufs support](http://docs.docker.io/en/latest/installation/kernel.html). If you're deploying new machines for the purpose of running Docker, this is a fairly easy requirement to meet.
13
+However, if you're adding Docker to an existing deployment, you may not have the flexibility to update and patch the kernel.
14
+
15
+Expanding Docker's kernel support is a priority. This includes running on older kernel versions,
16
+but also on kernels with no AUFS support, or with incomplete lxc capabilities.
17
+
18
+
19
+Cross-architecture support
20
+--------------------------
21
+
22
+Our goal is to make Docker run everywhere. However currently Docker only runs on x86_64 systems.
23
+We plan on expanding architecture support, so that Docker containers can be created and used on more architectures.
24
+
25
+
26
+Even more integrations
27
+----------------------
28
+
29
+We want Docker to be the secret ingredient that makes your existing tools more awesome.
30
+Thanks to this philosophy, Docker has already been integrated with
31
+[Puppet](http://forge.puppetlabs.com/garethr/docker),  [Chef](http://www.opscode.com/chef),
32
+[Openstack Nova](https://github.com/dotcloud/openstack-docker), [Jenkins](https://github.com/georgebashi/jenkins-docker-plugin),
33
+[DotCloud sandbox](http://github.com/dotcloud/sandbox), [Pallet](https://github.com/pallet/pallet-docker),
34
+[Strider CI](http://blog.frozenridge.co/next-generation-continuous-integration-deployment-with-dotclouds-docker-and-strider/)
35
+and even [Heroku buildpacks](https://github.com/progrium/buildstep).
36
+
37
+Expect Docker to integrate with even more of your favorite tools going forward, including:
38
+
39
+* Alternative storage backends such as ZFS, LVM or [BTRFS](github.com/dotcloud/docker/issues/443)
40
+* Alternative containerization backends such as [OpenVZ](http://openvz.org), Solaris Zones, BSD Jails and even plain Chroot.
41
+* Process managers like [Supervisord](http://supervisord.org/), [Runit](http://smarden.org/runit/), [Gaffer](https://gaffer.readthedocs.org/en/latest/#gaffer) and [Systemd](http://www.freedesktop.org/wiki/Software/systemd/)
42
+* Build and integration tools like Make, Maven, Scons, Jenkins, Buildbot and Cruise Control.
43
+* Configuration management tools like [Puppet](http://puppetlabs.com), [Chef](http://www.opscode.com/chef/) and [Salt](http://saltstack.org)
44
+* Personal development environments like [Vagrant](http://vagrantup.com), [Boxen](http://boxen.github.com/), [Koding](http://koding.com) and [Cloud9](http://c9.io).
45
+* Orchestration tools like [Zookeeper](http://zookeeper.apache.org/), [Mesos](http://incubator.apache.org/mesos/) and [Galaxy](https://github.com/ning/galaxy)
46
+* Infrastructure deployment tools like [Openstack](http://openstack.org), [Apache Cloudstack](http://apache.cloudstack.org), [Ganeti](https://code.google.com/p/ganeti/)
47
+
48
+
49
+Plugin API
50
+----------
51
+
52
+We want Docker to run everywhere, and to integrate with every devops tool.
53
+Those are ambitious goals, and the only way to reach them is with the Docker community.
54
+For the community to participate fully, we need an API which allows Docker to be deeply and easily customized.
55
+
56
+We are working on a plugin API which will make Docker very, very customization-friendly.
57
+We believe it will facilitate the integrations listed above - and many more we didn't even think about.
58
+
59
+Let us know if you want to start playing with the API before it's generally available.
60
+
61
+
62
+Externally mounted volumes
63
+--------------------------
64
+
65
+In 0.3 we [introduced data volumes](https://github.com/dotcloud/docker/wiki/Docker-0.3.0-release-note%2C-May-6-2013#data-volumes),
66
+a great mechanism for manipulating persistent data such as database files, log files, etc.
67
+
68
+Data volumes can be shared between containers, a powerful capability [which allows many advanced use cases](http://docs.docker.io/en/latest/examples/couchdb_data_volumes.html). In the future it will also be possible to share volumes between a container and the underlying host. This will make certain scenarios much easier, such as using a high-performance storage backend for your production database,
69
+making live development changes available to a container, etc.
70
+
71
+
72
+Better documentation
73
+--------------------
74
+
75
+We believe that great documentation is worth 10 features. We are often told that "Docker's documentation is great for a 2-month old project".
76
+Our goal is to make it great, period.
77
+
78
+If you have feedback on how to improve our documentation, please get in touch by replying to this email,
79
+or by [filing an issue](https://github.com/dotcloud/docker/issues). We always appreciate it!
80
+
81
+
82
+Production-ready
83
+----------------
84
+
85
+Docker is still alpha software, and not suited for production.
86
+We are working hard to get there, and we are confident that it will be possible within a few months.
87
+
88
+
89
+Advanced port redirections
90
+--------------------------
91
+
92
+Docker currently supports 2 flavors of port redirection: STATIC->STATIC (eg. "redirect public port 80 to private port 80")
93
+and RANDOM->STATIC (eg. "redirect any public port to private port 80").
94
+
95
+With these 2 flavors, docker can support the majority of backend programs out there. But some applications have more exotic
96
+requirements, generally to implement custom clustering techniques. These applications include Hadoop, MongoDB, Riak, RabbitMQ,
97
+Disco, and all programs relying on Erlang's OTP.
98
+
99
+To support these applications, Docker needs to support more advanced redirection flavors, including:
100
+
101
+* RANDOM->RANDOM
102
+* STATIC1->STATIC2
103
+
104
+These flavors should be implemented without breaking existing semantics, if at all possible.
0 105
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Daniel Mizyrycki <daniel@dotcloud.com>
... ...
@@ -18,7 +18,7 @@ import (
18 18
 )
19 19
 
20 20
 type Image struct {
21
-	Id              string    `json:"id"`
21
+	ID              string    `json:"id"`
22 22
 	Parent          string    `json:"parent,omitempty"`
23 23
 	Comment         string    `json:"comment,omitempty"`
24 24
 	Created         time.Time `json:"created"`
... ...
@@ -42,18 +42,17 @@ func LoadImage(root string) (*Image, error) {
42 42
 	if err := json.Unmarshal(jsonData, img); err != nil {
43 43
 		return nil, err
44 44
 	}
45
-	if err := ValidateId(img.Id); err != nil {
45
+	if err := ValidateID(img.ID); err != nil {
46 46
 		return nil, err
47 47
 	}
48 48
 	// Check that the filesystem layer exists
49 49
 	if stat, err := os.Stat(layerPath(root)); err != nil {
50 50
 		if os.IsNotExist(err) {
51
-			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.Id)
52
-		} else {
53
-			return nil, err
51
+			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
54 52
 		}
53
+		return nil, err
55 54
 	} else if !stat.IsDir() {
56
-		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.Id, layerPath(root))
55
+		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
57 56
 	}
58 57
 	return img, nil
59 58
 }
... ...
@@ -61,7 +60,7 @@ func LoadImage(root string) (*Image, error) {
61 61
 func StoreImage(img *Image, layerData Archive, root string, store bool) error {
62 62
 	// Check that root doesn't already exist
63 63
 	if _, err := os.Stat(root); err == nil {
64
-		return fmt.Errorf("Image %s already exists", img.Id)
64
+		return fmt.Errorf("Image %s already exists", img.ID)
65 65
 	} else if !os.IsNotExist(err) {
66 66
 		return err
67 67
 	}
... ...
@@ -127,6 +126,8 @@ func MountAUFS(ro []string, rw string, target string) error {
127 127
 	}
128 128
 	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
129 129
 
130
+	branches += ",xino=/dev/shm/aufs.xino"
131
+
130 132
 	//if error, try to load aufs kernel module
131 133
 	if err := mount("none", target, "aufs", 0, branches); err != nil {
132 134
 		log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
... ...
@@ -181,11 +182,11 @@ func (image *Image) Changes(rw string) ([]Change, error) {
181 181
 	return Changes(layers, rw)
182 182
 }
183 183
 
184
-func (image *Image) ShortId() string {
185
-	return utils.TruncateId(image.Id)
184
+func (image *Image) ShortID() string {
185
+	return utils.TruncateID(image.ID)
186 186
 }
187 187
 
188
-func ValidateId(id string) error {
188
+func ValidateID(id string) error {
189 189
 	if id == "" {
190 190
 		return fmt.Errorf("Image id can't be empty")
191 191
 	}
... ...
@@ -195,7 +196,7 @@ func ValidateId(id string) error {
195 195
 	return nil
196 196
 }
197 197
 
198
-func GenerateId() string {
198
+func GenerateID() string {
199 199
 	id := make([]byte, 32)
200 200
 	_, err := io.ReadFull(rand.Reader, id)
201 201
 	if err != nil {
... ...
@@ -241,7 +242,7 @@ func (img *Image) layers() ([]string, error) {
241 241
 		return nil, e
242 242
 	}
243 243
 	if len(list) == 0 {
244
-		return nil, fmt.Errorf("No layer found for image %s\n", img.Id)
244
+		return nil, fmt.Errorf("No layer found for image %s\n", img.ID)
245 245
 	}
246 246
 	return list, nil
247 247
 }
... ...
@@ -276,7 +277,7 @@ func (img *Image) root() (string, error) {
276 276
 	if img.graph == nil {
277 277
 		return "", fmt.Errorf("Can't lookup root of unregistered image")
278 278
 	}
279
-	return img.graph.imageRoot(img.Id), nil
279
+	return img.graph.imageRoot(img.ID), nil
280 280
 }
281 281
 
282 282
 // Return the path of an image's layer
... ...
@@ -289,8 +290,8 @@ func (img *Image) layer() (string, error) {
289 289
 }
290 290
 
291 291
 func (img *Image) Checksum() (string, error) {
292
-	img.graph.checksumLock[img.Id].Lock()
293
-	defer img.graph.checksumLock[img.Id].Unlock()
292
+	img.graph.checksumLock[img.ID].Lock()
293
+	defer img.graph.checksumLock[img.ID].Unlock()
294 294
 
295 295
 	root, err := img.root()
296 296
 	if err != nil {
... ...
@@ -301,7 +302,7 @@ func (img *Image) Checksum() (string, error) {
301 301
 	if err != nil {
302 302
 		return "", err
303 303
 	}
304
-	if checksum, ok := checksums[img.Id]; ok {
304
+	if checksum, ok := checksums[img.ID]; ok {
305 305
 		return checksum, nil
306 306
 	}
307 307
 
... ...
@@ -352,7 +353,7 @@ func (img *Image) Checksum() (string, error) {
352 352
 		return "", err
353 353
 	}
354 354
 
355
-	checksums[img.Id] = hash
355
+	checksums[img.ID] = hash
356 356
 
357 357
 	// Dump the checksums to disc
358 358
 	if err := img.graph.storeChecksums(checksums); err != nil {
... ...
@@ -363,7 +364,7 @@ func (img *Image) Checksum() (string, error) {
363 363
 }
364 364
 
365 365
 // Build an Image object from raw json data
366
-func NewImgJson(src []byte) (*Image, error) {
366
+func NewImgJSON(src []byte) (*Image, error) {
367 367
 	ret := &Image{}
368 368
 
369 369
 	utils.Debugf("Json string: {%s}\n", src)
... ...
@@ -19,7 +19,7 @@ lxc.network.flags = up
19 19
 lxc.network.link = {{.NetworkSettings.Bridge}}
20 20
 lxc.network.name = eth0
21 21
 lxc.network.mtu = 1500
22
-lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}}
22
+lxc.network.ipv4 = {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}
23 23
 
24 24
 # root filesystem
25 25
 {{$ROOTFS := .RootfsPath}}
... ...
@@ -52,7 +52,7 @@ func ipToInt(ip net.IP) int32 {
52 52
 }
53 53
 
54 54
 // Converts 32 bit integer into a 4 bytes IP address
55
-func intToIp(n int32) net.IP {
55
+func intToIP(n int32) net.IP {
56 56
 	b := make([]byte, 4)
57 57
 	binary.BigEndian.PutUint32(b, uint32(n))
58 58
 	return net.IP(b)
... ...
@@ -132,9 +132,8 @@ func CreateBridgeIface(ifaceName string) error {
132 132
 	}
133 133
 	if ifaceAddr == "" {
134 134
 		return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName)
135
-	} else {
136
-		utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
137 135
 	}
136
+	utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
138 137
 
139 138
 	if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil {
140 139
 		return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output)
... ...
@@ -258,7 +257,7 @@ func proxy(listener net.Listener, proto, address string) error {
258 258
 		utils.Debugf("Connected to backend, splicing")
259 259
 		splice(src, dst)
260 260
 	}
261
-	return nil
261
+	panic("Unreachable")
262 262
 }
263 263
 
264 264
 func halfSplice(dst, src net.Conn) error {
... ...
@@ -398,7 +397,7 @@ func (alloc *IPAllocator) run() {
398 398
 			}
399 399
 		}
400 400
 
401
-		ip := allocatedIP{ip: intToIp(newNum)}
401
+		ip := allocatedIP{ip: intToIP(newNum)}
402 402
 		if inUse {
403 403
 			ip.err = errors.New("No unallocated IP available")
404 404
 		}
... ...
@@ -465,11 +464,11 @@ func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
465 465
 		return nil, err
466 466
 	}
467 467
 	// Allocate a random port if Frontend==0
468
-	if extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend); err != nil {
468
+	extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend)
469
+	if err != nil {
469 470
 		return nil, err
470
-	} else {
471
-		nat.Frontend = extPort
472 471
 	}
472
+	nat.Frontend = extPort
473 473
 	if err := iface.manager.portMapper.Map(nat.Frontend, net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}); err != nil {
474 474
 		iface.manager.portAllocator.Release(nat.Frontend)
475 475
 		return nil, err
... ...
@@ -486,20 +485,38 @@ type Nat struct {
486 486
 
487 487
 func parseNat(spec string) (*Nat, error) {
488 488
 	var nat Nat
489
-	// If spec starts with ':', external and internal ports must be the same.
490
-	// This might fail if the requested external port is not available.
491
-	var sameFrontend bool
492
-	if spec[0] == ':' {
493
-		sameFrontend = true
494
-		spec = spec[1:]
495
-	}
496
-	port, err := strconv.ParseUint(spec, 10, 16)
497
-	if err != nil {
498
-		return nil, err
499
-	}
500
-	nat.Backend = int(port)
501
-	if sameFrontend {
502
-		nat.Frontend = nat.Backend
489
+
490
+	if strings.Contains(spec, ":") {
491
+		specParts := strings.Split(spec, ":")
492
+		if len(specParts) != 2 {
493
+			return nil, fmt.Errorf("Invalid port format.")
494
+		}
495
+		// If spec starts with ':', external and internal ports must be the same.
496
+		// This might fail if the requested external port is not available.
497
+		var sameFrontend bool
498
+		if len(specParts[0]) == 0 {
499
+			sameFrontend = true
500
+		} else {
501
+			front, err := strconv.ParseUint(specParts[0], 10, 16)
502
+			if err != nil {
503
+				return nil, err
504
+			}
505
+			nat.Frontend = int(front)
506
+		}
507
+		back, err := strconv.ParseUint(specParts[1], 10, 16)
508
+		if err != nil {
509
+			return nil, err
510
+		}
511
+		nat.Backend = int(back)
512
+		if sameFrontend {
513
+			nat.Frontend = nat.Backend
514
+		}
515
+	} else {
516
+		port, err := strconv.ParseUint(spec, 10, 16)
517
+		if err != nil {
518
+			return nil, err
519
+		}
520
+		nat.Backend = int(port)
503 521
 	}
504 522
 	nat.Proto = "tcp"
505 523
 	return &nat, nil
... ...
@@ -18,6 +18,32 @@ func TestIptables(t *testing.T) {
18 18
 	}
19 19
 }
20 20
 
21
+func TestParseNat(t *testing.T) {
22
+	if nat, err := parseNat("4500"); err == nil {
23
+		if nat.Frontend != 0 || nat.Backend != 4500 {
24
+			t.Errorf("-p 4500 should produce 0->4500, got %d->%d", nat.Frontend, nat.Backend)
25
+		}
26
+	} else {
27
+		t.Fatal(err)
28
+	}
29
+
30
+	if nat, err := parseNat(":4501"); err == nil {
31
+		if nat.Frontend != 4501 || nat.Backend != 4501 {
32
+			t.Errorf("-p :4501 should produce 4501->4501, got %d->%d", nat.Frontend, nat.Backend)
33
+		}
34
+	} else {
35
+		t.Fatal(err)
36
+	}
37
+
38
+	if nat, err := parseNat("4502:4503"); err == nil {
39
+		if nat.Frontend != 4502 || nat.Backend != 4503 {
40
+			t.Errorf("-p 4502:4503 should produce 4502->4503, got %d->%d", nat.Frontend, nat.Backend)
41
+		}
42
+	} else {
43
+		t.Fatal(err)
44
+	}
45
+}
46
+
21 47
 func TestPortAllocation(t *testing.T) {
22 48
 	allocator, err := newPortAllocator()
23 49
 	if err != nil {
... ...
@@ -137,7 +163,7 @@ func TestConversion(t *testing.T) {
137 137
 	if i == 0 {
138 138
 		t.Fatal("converted to zero")
139 139
 	}
140
-	conv := intToIp(i)
140
+	conv := intToIP(i)
141 141
 	if !ip.Equal(conv) {
142 142
 		t.Error(conv.String())
143 143
 	}
... ...
@@ -15,7 +15,7 @@ import (
15 15
 	"strings"
16 16
 )
17 17
 
18
-var ErrAlreadyExists error = errors.New("Image already exists")
18
+var ErrAlreadyExists = errors.New("Image already exists")
19 19
 
20 20
 func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) {
21 21
 	for _, cookie := range c.Jar.Cookies(req.URL) {
... ...
@@ -64,7 +64,11 @@ func (r *Registry) LookupRemoteImage(imgId, registry string, authConfig *auth.Au
64 64
 	}
65 65
 	req.SetBasicAuth(authConfig.Username, authConfig.Password)
66 66
 	res, err := rt.RoundTrip(req)
67
-	return err == nil && res.StatusCode == 307
67
+	if err != nil {
68
+		return false
69
+	}
70
+	res.Body.Close()
71
+	return res.StatusCode == 307
68 72
 }
69 73
 
70 74
 func (r *Registry) getImagesInRepository(repository string, authConfig *auth.AuthConfig) ([]map[string]string, error) {
... ...
@@ -103,8 +107,8 @@ func (r *Registry) getImagesInRepository(repository string, authConfig *auth.Aut
103 103
 
104 104
 // Retrieve an image from the Registry.
105 105
 // Returns the Image object as well as the layer as an Archive (io.Reader)
106
-func (r *Registry) GetRemoteImageJson(imgId, registry string, token []string) ([]byte, error) {
107
-	// Get the Json
106
+func (r *Registry) GetRemoteImageJSON(imgId, registry string, token []string) ([]byte, error) {
107
+	// Get the JSON
108 108
 	req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil)
109 109
 	if err != nil {
110 110
 		return nil, fmt.Errorf("Failed to download json: %s", err)
... ...
@@ -152,21 +156,24 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [
152 152
 		}
153 153
 		req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
154 154
 		res, err := r.client.Do(req)
155
-		defer res.Body.Close()
156 155
 		utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint)
157
-		if err != nil || (res.StatusCode != 200 && res.StatusCode != 404) {
156
+		if err != nil {
157
+			return nil, err
158
+		}
159
+		defer res.Body.Close()
160
+
161
+		if res.StatusCode != 200 && res.StatusCode != 404 {
158 162
 			continue
159 163
 		} else if res.StatusCode == 404 {
160 164
 			return nil, fmt.Errorf("Repository not found")
161 165
 		}
162 166
 
163 167
 		result := make(map[string]string)
164
-
165
-		rawJson, err := ioutil.ReadAll(res.Body)
168
+		rawJSON, err := ioutil.ReadAll(res.Body)
166 169
 		if err != nil {
167 170
 			return nil, err
168 171
 		}
169
-		if err := json.Unmarshal(rawJson, &result); err != nil {
172
+		if err := json.Unmarshal(rawJSON, &result); err != nil {
170 173
 			return nil, err
171 174
 		}
172 175
 		return result, nil
... ...
@@ -212,19 +219,19 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
212 212
 		return nil, fmt.Errorf("Index response didn't contain any endpoints")
213 213
 	}
214 214
 
215
-	checksumsJson, err := ioutil.ReadAll(res.Body)
215
+	checksumsJSON, err := ioutil.ReadAll(res.Body)
216 216
 	if err != nil {
217 217
 		return nil, err
218 218
 	}
219 219
 	remoteChecksums := []*ImgData{}
220
-	if err := json.Unmarshal(checksumsJson, &remoteChecksums); err != nil {
220
+	if err := json.Unmarshal(checksumsJSON, &remoteChecksums); err != nil {
221 221
 		return nil, err
222 222
 	}
223 223
 
224 224
 	// Forge a better object from the retrieved data
225 225
 	imgsData := make(map[string]*ImgData)
226 226
 	for _, elem := range remoteChecksums {
227
-		imgsData[elem.Id] = elem
227
+		imgsData[elem.ID] = elem
228 228
 	}
229 229
 
230 230
 	return &RepositoryData{
... ...
@@ -235,10 +242,10 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
235 235
 }
236 236
 
237 237
 // Push a local image to the registry
238
-func (r *Registry) PushImageJsonRegistry(imgData *ImgData, jsonRaw []byte, registry string, token []string) error {
238
+func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, registry string, token []string) error {
239 239
 	registry = "https://" + registry + "/v1"
240 240
 	// FIXME: try json with UTF8
241
-	req, err := http.NewRequest("PUT", registry+"/images/"+imgData.Id+"/json", strings.NewReader(string(jsonRaw)))
241
+	req, err := http.NewRequest("PUT", registry+"/images/"+imgData.ID+"/json", strings.NewReader(string(jsonRaw)))
242 242
 	if err != nil {
243 243
 		return err
244 244
 	}
... ...
@@ -246,7 +253,7 @@ func (r *Registry) PushImageJsonRegistry(imgData *ImgData, jsonRaw []byte, regis
246 246
 	req.Header.Set("Authorization", "Token "+strings.Join(token, ","))
247 247
 	req.Header.Set("X-Docker-Checksum", imgData.Checksum)
248 248
 
249
-	utils.Debugf("Setting checksum for %s: %s", imgData.Id, imgData.Checksum)
249
+	utils.Debugf("Setting checksum for %s: %s", imgData.ID, imgData.Checksum)
250 250
 	res, err := doWithCookies(r.client, req)
251 251
 	if err != nil {
252 252
 		return fmt.Errorf("Failed to upload metadata: %s", err)
... ...
@@ -321,8 +328,8 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token
321 321
 	return nil
322 322
 }
323 323
 
324
-func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) {
325
-	imgListJson, err := json.Marshal(imgList)
324
+func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) {
325
+	imgListJSON, err := json.Marshal(imgList)
326 326
 	if err != nil {
327 327
 		return nil, err
328 328
 	}
... ...
@@ -331,14 +338,14 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
331 331
 		suffix = "images"
332 332
 	}
333 333
 
334
-	utils.Debugf("Image list pushed to index:\n%s\n", imgListJson)
334
+	utils.Debugf("Image list pushed to index:\n%s\n", imgListJSON)
335 335
 
336
-	req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJson))
336
+	req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJSON))
337 337
 	if err != nil {
338 338
 		return nil, err
339 339
 	}
340 340
 	req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
341
-	req.ContentLength = int64(len(imgListJson))
341
+	req.ContentLength = int64(len(imgListJSON))
342 342
 	req.Header.Set("X-Docker-Token", "true")
343 343
 
344 344
 	res, err := r.client.Do(req)
... ...
@@ -350,12 +357,12 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
350 350
 	// Redirect if necessary
351 351
 	for res.StatusCode >= 300 && res.StatusCode < 400 {
352 352
 		utils.Debugf("Redirected to %s\n", res.Header.Get("Location"))
353
-		req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJson))
353
+		req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJSON))
354 354
 		if err != nil {
355 355
 			return nil, err
356 356
 		}
357 357
 		req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
358
-		req.ContentLength = int64(len(imgListJson))
358
+		req.ContentLength = int64(len(imgListJSON))
359 359
 		req.Header.Set("X-Docker-Token", "true")
360 360
 
361 361
 		res, err = r.client.Do(req)
... ...
@@ -389,11 +396,11 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
389 389
 	}
390 390
 	if validate {
391 391
 		if res.StatusCode != 204 {
392
-			if errBody, err := ioutil.ReadAll(res.Body); err != nil {
392
+			errBody, err := ioutil.ReadAll(res.Body)
393
+			if err != nil {
393 394
 				return nil, err
394
-			} else {
395
-				return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody)
396 395
 			}
396
+			return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody)
397 397
 		}
398 398
 	}
399 399
 
... ...
@@ -456,7 +463,7 @@ type RepositoryData struct {
456 456
 }
457 457
 
458 458
 type ImgData struct {
459
-	Id       string `json:"id"`
459
+	ID       string `json:"id"`
460 460
 	Checksum string `json:"checksum,omitempty"`
461 461
 	Tag      string `json:",omitempty"`
462 462
 }
... ...
@@ -470,9 +477,16 @@ func NewRegistry(root string) *Registry {
470 470
 	// If the auth file does not exist, keep going
471 471
 	authConfig, _ := auth.LoadConfig(root)
472 472
 
473
+	httpTransport := &http.Transport{
474
+		DisableKeepAlives: true,
475
+		Proxy: http.ProxyFromEnvironment,
476
+	}
477
+
473 478
 	r := &Registry{
474 479
 		authConfig: authConfig,
475
-		client:     &http.Client{},
480
+		client: &http.Client{
481
+			Transport: httpTransport,
482
+		},
476 483
 	}
477 484
 	r.client.Jar = cookiejar.NewCookieJar()
478 485
 	return r
... ...
@@ -51,7 +51,7 @@ func (runtime *Runtime) List() []*Container {
51 51
 func (runtime *Runtime) getContainerElement(id string) *list.Element {
52 52
 	for e := runtime.containers.Front(); e != nil; e = e.Next() {
53 53
 		container := e.Value.(*Container)
54
-		if container.Id == id {
54
+		if container.ID == id {
55 55
 			return e
56 56
 		}
57 57
 	}
... ...
@@ -83,8 +83,8 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
83 83
 	if err := container.FromDisk(); err != nil {
84 84
 		return nil, err
85 85
 	}
86
-	if container.Id != id {
87
-		return container, fmt.Errorf("Container %s is stored at %s", container.Id, id)
86
+	if container.ID != id {
87
+		return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
88 88
 	}
89 89
 	if container.State.Running {
90 90
 		container.State.Ghost = true
... ...
@@ -95,12 +95,12 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
95 95
 	return container, nil
96 96
 }
97 97
 
98
-// Register makes a container object usable by the runtime as <container.Id>
98
+// Register makes a container object usable by the runtime as <container.ID>
99 99
 func (runtime *Runtime) Register(container *Container) error {
100
-	if container.runtime != nil || runtime.Exists(container.Id) {
100
+	if container.runtime != nil || runtime.Exists(container.ID) {
101 101
 		return fmt.Errorf("Container is already loaded")
102 102
 	}
103
-	if err := validateId(container.Id); err != nil {
103
+	if err := validateID(container.ID); err != nil {
104 104
 		return err
105 105
 	}
106 106
 
... ...
@@ -123,7 +123,7 @@ func (runtime *Runtime) Register(container *Container) error {
123 123
 	}
124 124
 	// done
125 125
 	runtime.containers.PushBack(container)
126
-	runtime.idIndex.Add(container.Id)
126
+	runtime.idIndex.Add(container.ID)
127 127
 
128 128
 	// When we actually restart, Start() do the monitoring.
129 129
 	// However, when we simply 'reattach', we have to restart a monitor
... ...
@@ -133,25 +133,25 @@ func (runtime *Runtime) Register(container *Container) error {
133 133
 	//        if so, then we need to restart monitor and init a new lock
134 134
 	// If the container is supposed to be running, make sure of it
135 135
 	if container.State.Running {
136
-		if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
136
+		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
137
+		if err != nil {
137 138
 			return err
138
-		} else {
139
-			if !strings.Contains(string(output), "RUNNING") {
140
-				utils.Debugf("Container %s was supposed to be running be is not.", container.Id)
141
-				if runtime.autoRestart {
142
-					utils.Debugf("Restarting")
143
-					container.State.Ghost = false
144
-					container.State.setStopped(0)
145
-					if err := container.Start(); err != nil {
146
-						return err
147
-					}
148
-					nomonitor = true
149
-				} else {
150
-					utils.Debugf("Marking as stopped")
151
-					container.State.setStopped(-127)
152
-					if err := container.ToDisk(); err != nil {
153
-						return err
154
-					}
139
+		}
140
+		if !strings.Contains(string(output), "RUNNING") {
141
+			utils.Debugf("Container %s was supposed to be running be is not.", container.ID)
142
+			if runtime.autoRestart {
143
+				utils.Debugf("Restarting")
144
+				container.State.Ghost = false
145
+				container.State.setStopped(0)
146
+				if err := container.Start(); err != nil {
147
+					return err
148
+				}
149
+				nomonitor = true
150
+			} else {
151
+				utils.Debugf("Marking as stopped")
152
+				container.State.setStopped(-127)
153
+				if err := container.ToDisk(); err != nil {
154
+					return err
155 155
 				}
156 156
 			}
157 157
 		}
... ...
@@ -182,9 +182,9 @@ func (runtime *Runtime) Destroy(container *Container) error {
182 182
 		return fmt.Errorf("The given container is <nil>")
183 183
 	}
184 184
 
185
-	element := runtime.getContainerElement(container.Id)
185
+	element := runtime.getContainerElement(container.ID)
186 186
 	if element == nil {
187
-		return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.Id)
187
+		return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
188 188
 	}
189 189
 
190 190
 	if err := container.Stop(3); err != nil {
... ...
@@ -194,14 +194,14 @@ func (runtime *Runtime) Destroy(container *Container) error {
194 194
 		return err
195 195
 	} else if mounted {
196 196
 		if err := container.Unmount(); err != nil {
197
-			return fmt.Errorf("Unable to unmount container %v: %v", container.Id, err)
197
+			return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
198 198
 		}
199 199
 	}
200 200
 	// Deregister the container before removing its directory, to avoid race conditions
201
-	runtime.idIndex.Delete(container.Id)
201
+	runtime.idIndex.Delete(container.ID)
202 202
 	runtime.containers.Remove(element)
203 203
 	if err := os.RemoveAll(container.root); err != nil {
204
-		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.Id, err)
204
+		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
205 205
 	}
206 206
 	return nil
207 207
 }
... ...
@@ -218,7 +218,7 @@ func (runtime *Runtime) restore() error {
218 218
 			utils.Debugf("Failed to load container %v: %v", id, err)
219 219
 			continue
220 220
 		}
221
-		utils.Debugf("Loaded container %v", container.Id)
221
+		utils.Debugf("Loaded container %v", container.ID)
222 222
 	}
223 223
 	return nil
224 224
 }
... ...
@@ -120,7 +120,7 @@ func TestRuntimeCreate(t *testing.T) {
120 120
 	builder := NewBuilder(runtime)
121 121
 
122 122
 	container, err := builder.Create(&Config{
123
-		Image: GetTestImage(runtime).Id,
123
+		Image: GetTestImage(runtime).ID,
124 124
 		Cmd:   []string{"ls", "-al"},
125 125
 	},
126 126
 	)
... ...
@@ -140,29 +140,29 @@ func TestRuntimeCreate(t *testing.T) {
140 140
 	}
141 141
 
142 142
 	// Make sure the container List() returns is the right one
143
-	if runtime.List()[0].Id != container.Id {
143
+	if runtime.List()[0].ID != container.ID {
144 144
 		t.Errorf("Unexpected container %v returned by List", runtime.List()[0])
145 145
 	}
146 146
 
147 147
 	// Make sure we can get the container with Get()
148
-	if runtime.Get(container.Id) == nil {
148
+	if runtime.Get(container.ID) == nil {
149 149
 		t.Errorf("Unable to get newly created container")
150 150
 	}
151 151
 
152 152
 	// Make sure it is the right container
153
-	if runtime.Get(container.Id) != container {
153
+	if runtime.Get(container.ID) != container {
154 154
 		t.Errorf("Get() returned the wrong container")
155 155
 	}
156 156
 
157 157
 	// Make sure Exists returns it as existing
158
-	if !runtime.Exists(container.Id) {
158
+	if !runtime.Exists(container.ID) {
159 159
 		t.Errorf("Exists() returned false for a newly created container")
160 160
 	}
161 161
 
162 162
 	// Make sure crete with bad parameters returns an error
163 163
 	_, err = builder.Create(
164 164
 		&Config{
165
-			Image: GetTestImage(runtime).Id,
165
+			Image: GetTestImage(runtime).ID,
166 166
 		},
167 167
 	)
168 168
 	if err == nil {
... ...
@@ -171,7 +171,7 @@ func TestRuntimeCreate(t *testing.T) {
171 171
 
172 172
 	_, err = builder.Create(
173 173
 		&Config{
174
-			Image: GetTestImage(runtime).Id,
174
+			Image: GetTestImage(runtime).ID,
175 175
 			Cmd:   []string{},
176 176
 		},
177 177
 	)
... ...
@@ -187,7 +187,7 @@ func TestDestroy(t *testing.T) {
187 187
 	}
188 188
 	defer nuke(runtime)
189 189
 	container, err := NewBuilder(runtime).Create(&Config{
190
-		Image: GetTestImage(runtime).Id,
190
+		Image: GetTestImage(runtime).ID,
191 191
 		Cmd:   []string{"ls", "-al"},
192 192
 	},
193 193
 	)
... ...
@@ -210,7 +210,7 @@ func TestDestroy(t *testing.T) {
210 210
 	}
211 211
 
212 212
 	// Make sure runtime.Get() refuses to return the unexisting container
213
-	if runtime.Get(container.Id) != nil {
213
+	if runtime.Get(container.ID) != nil {
214 214
 		t.Errorf("Unable to get newly created container")
215 215
 	}
216 216
 
... ...
@@ -237,7 +237,7 @@ func TestGet(t *testing.T) {
237 237
 	builder := NewBuilder(runtime)
238 238
 
239 239
 	container1, err := builder.Create(&Config{
240
-		Image: GetTestImage(runtime).Id,
240
+		Image: GetTestImage(runtime).ID,
241 241
 		Cmd:   []string{"ls", "-al"},
242 242
 	},
243 243
 	)
... ...
@@ -247,7 +247,7 @@ func TestGet(t *testing.T) {
247 247
 	defer runtime.Destroy(container1)
248 248
 
249 249
 	container2, err := builder.Create(&Config{
250
-		Image: GetTestImage(runtime).Id,
250
+		Image: GetTestImage(runtime).ID,
251 251
 		Cmd:   []string{"ls", "-al"},
252 252
 	},
253 253
 	)
... ...
@@ -257,7 +257,7 @@ func TestGet(t *testing.T) {
257 257
 	defer runtime.Destroy(container2)
258 258
 
259 259
 	container3, err := builder.Create(&Config{
260
-		Image: GetTestImage(runtime).Id,
260
+		Image: GetTestImage(runtime).ID,
261 261
 		Cmd:   []string{"ls", "-al"},
262 262
 	},
263 263
 	)
... ...
@@ -266,16 +266,16 @@ func TestGet(t *testing.T) {
266 266
 	}
267 267
 	defer runtime.Destroy(container3)
268 268
 
269
-	if runtime.Get(container1.Id) != container1 {
270
-		t.Errorf("Get(test1) returned %v while expecting %v", runtime.Get(container1.Id), container1)
269
+	if runtime.Get(container1.ID) != container1 {
270
+		t.Errorf("Get(test1) returned %v while expecting %v", runtime.Get(container1.ID), container1)
271 271
 	}
272 272
 
273
-	if runtime.Get(container2.Id) != container2 {
274
-		t.Errorf("Get(test2) returned %v while expecting %v", runtime.Get(container2.Id), container2)
273
+	if runtime.Get(container2.ID) != container2 {
274
+		t.Errorf("Get(test2) returned %v while expecting %v", runtime.Get(container2.ID), container2)
275 275
 	}
276 276
 
277
-	if runtime.Get(container3.Id) != container3 {
278
-		t.Errorf("Get(test3) returned %v while expecting %v", runtime.Get(container3.Id), container3)
277
+	if runtime.Get(container3.ID) != container3 {
278
+		t.Errorf("Get(test3) returned %v while expecting %v", runtime.Get(container3.ID), container3)
279 279
 	}
280 280
 
281 281
 }
... ...
@@ -283,7 +283,7 @@ func TestGet(t *testing.T) {
283 283
 func findAvailalblePort(runtime *Runtime, port int) (*Container, error) {
284 284
 	strPort := strconv.Itoa(port)
285 285
 	container, err := NewBuilder(runtime).Create(&Config{
286
-		Image:     GetTestImage(runtime).Id,
286
+		Image:     GetTestImage(runtime).ID,
287 287
 		Cmd:       []string{"sh", "-c", "echo well hello there | nc -l -p " + strPort},
288 288
 		PortSpecs: []string{strPort},
289 289
 	},
... ...
@@ -379,7 +379,7 @@ func TestRestore(t *testing.T) {
379 379
 
380 380
 	// Create a container with one instance of docker
381 381
 	container1, err := builder.Create(&Config{
382
-		Image: GetTestImage(runtime1).Id,
382
+		Image: GetTestImage(runtime1).ID,
383 383
 		Cmd:   []string{"ls", "-al"},
384 384
 	},
385 385
 	)
... ...
@@ -390,7 +390,7 @@ func TestRestore(t *testing.T) {
390 390
 
391 391
 	// Create a second container meant to be killed
392 392
 	container2, err := builder.Create(&Config{
393
-		Image:     GetTestImage(runtime1).Id,
393
+		Image:     GetTestImage(runtime1).ID,
394 394
 		Cmd:       []string{"/bin/cat"},
395 395
 		OpenStdin: true,
396 396
 	},
... ...
@@ -406,7 +406,7 @@ func TestRestore(t *testing.T) {
406 406
 	}
407 407
 
408 408
 	if !container2.State.Running {
409
-		t.Fatalf("Container %v should appear as running but isn't", container2.Id)
409
+		t.Fatalf("Container %v should appear as running but isn't", container2.ID)
410 410
 	}
411 411
 
412 412
 	// Simulate a crash/manual quit of dockerd: process dies, states stays 'Running'
... ...
@@ -426,7 +426,7 @@ func TestRestore(t *testing.T) {
426 426
 	}
427 427
 
428 428
 	if !container2.State.Running {
429
-		t.Fatalf("Container %v should appear as running but isn't", container2.Id)
429
+		t.Fatalf("Container %v should appear as running but isn't", container2.ID)
430 430
 	}
431 431
 
432 432
 	// Here are are simulating a docker restart - that is, reloading all containers
... ...
@@ -442,14 +442,14 @@ func TestRestore(t *testing.T) {
442 442
 	runningCount := 0
443 443
 	for _, c := range runtime2.List() {
444 444
 		if c.State.Running {
445
-			t.Errorf("Running container found: %v (%v)", c.Id, c.Path)
445
+			t.Errorf("Running container found: %v (%v)", c.ID, c.Path)
446 446
 			runningCount++
447 447
 		}
448 448
 	}
449 449
 	if runningCount != 0 {
450 450
 		t.Fatalf("Expected 0 container alive, %d found", runningCount)
451 451
 	}
452
-	container3 := runtime2.Get(container1.Id)
452
+	container3 := runtime2.Get(container1.ID)
453 453
 	if container3 == nil {
454 454
 		t.Fatal("Unable to Get container")
455 455
 	}
... ...
@@ -1,6 +1,7 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"fmt"
5 6
 	"github.com/dotcloud/docker/auth"
6 7
 	"github.com/dotcloud/docker/registry"
... ...
@@ -16,10 +17,10 @@ import (
16 16
 	"strings"
17 17
 )
18 18
 
19
-func (srv *Server) DockerVersion() ApiVersion {
20
-	return ApiVersion{
19
+func (srv *Server) DockerVersion() APIVersion {
20
+	return APIVersion{
21 21
 		Version:   VERSION,
22
-		GitCommit: GIT_COMMIT,
22
+		GitCommit: GITCOMMIT,
23 23
 		GoVersion: runtime.Version(),
24 24
 	}
25 25
 }
... ...
@@ -52,20 +53,17 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
52 52
 	return fmt.Errorf("No such container: %s", name)
53 53
 }
54 54
 
55
-func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) {
55
+func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
56 56
 
57 57
 	results, err := registry.NewRegistry(srv.runtime.root).SearchRepositories(term)
58 58
 	if err != nil {
59 59
 		return nil, err
60 60
 	}
61 61
 
62
-	var outs []ApiSearch
62
+	var outs []APISearch
63 63
 	for _, repo := range results.Results {
64
-		var out ApiSearch
64
+		var out APISearch
65 65
 		out.Description = repo["description"]
66
-		if len(out.Description) > 45 {
67
-			out.Description = utils.Trunc(out.Description, 42) + "..."
68
-		}
69 66
 		out.Name = repo["name"]
70 67
 		outs = append(outs, out)
71 68
 	}
... ...
@@ -85,7 +83,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
85 85
 	}
86 86
 	defer file.Body.Close()
87 87
 
88
-	config, _, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, srv.runtime.capabilities)
88
+	config, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities)
89 89
 	if err != nil {
90 90
 		return "", err
91 91
 	}
... ...
@@ -104,8 +102,8 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
104 104
 	if err != nil {
105 105
 		return "", err
106 106
 	}
107
-	out.Write(sf.FormatStatus(img.Id))
108
-	return img.ShortId(), nil
107
+	out.Write(sf.FormatStatus(img.ID))
108
+	return img.ShortID(), nil
109 109
 }
110 110
 
111 111
 func (srv *Server) ImagesViz(out io.Writer) error {
... ...
@@ -125,9 +123,9 @@ func (srv *Server) ImagesViz(out io.Writer) error {
125 125
 			return fmt.Errorf("Error while getting parent image: %v", err)
126 126
 		}
127 127
 		if parentImage != nil {
128
-			out.Write([]byte(" \"" + parentImage.ShortId() + "\" -> \"" + image.ShortId() + "\"\n"))
128
+			out.Write([]byte(" \"" + parentImage.ShortID() + "\" -> \"" + image.ShortID() + "\"\n"))
129 129
 		} else {
130
-			out.Write([]byte(" base -> \"" + image.ShortId() + "\" [style=invis]\n"))
130
+			out.Write([]byte(" base -> \"" + image.ShortID() + "\" [style=invis]\n"))
131 131
 		}
132 132
 	}
133 133
 
... ...
@@ -135,7 +133,7 @@ func (srv *Server) ImagesViz(out io.Writer) error {
135 135
 
136 136
 	for name, repository := range srv.runtime.repositories.Repositories {
137 137
 		for tag, id := range repository {
138
-			reporefs[utils.TruncateId(id)] = append(reporefs[utils.TruncateId(id)], fmt.Sprintf("%s:%s", name, tag))
138
+			reporefs[utils.TruncateID(id)] = append(reporefs[utils.TruncateID(id)], fmt.Sprintf("%s:%s", name, tag))
139 139
 		}
140 140
 	}
141 141
 
... ...
@@ -146,7 +144,7 @@ func (srv *Server) ImagesViz(out io.Writer) error {
146 146
 	return nil
147 147
 }
148 148
 
149
-func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
149
+func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
150 150
 	var (
151 151
 		allImages map[string]*Image
152 152
 		err       error
... ...
@@ -159,13 +157,13 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
159 159
 	if err != nil {
160 160
 		return nil, err
161 161
 	}
162
-	outs := []ApiImages{} //produce [] when empty instead of 'null'
162
+	outs := []APIImages{} //produce [] when empty instead of 'null'
163 163
 	for name, repository := range srv.runtime.repositories.Repositories {
164 164
 		if filter != "" && name != filter {
165 165
 			continue
166 166
 		}
167 167
 		for tag, id := range repository {
168
-			var out ApiImages
168
+			var out APIImages
169 169
 			image, err := srv.runtime.graph.Get(id)
170 170
 			if err != nil {
171 171
 				log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
... ...
@@ -174,7 +172,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
174 174
 			delete(allImages, id)
175 175
 			out.Repository = name
176 176
 			out.Tag = tag
177
-			out.Id = image.Id
177
+			out.ID = image.ID
178 178
 			out.Created = image.Created.Unix()
179 179
 			outs = append(outs, out)
180 180
 		}
... ...
@@ -182,8 +180,8 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
182 182
 	// Display images which aren't part of a
183 183
 	if filter == "" {
184 184
 		for _, image := range allImages {
185
-			var out ApiImages
186
-			out.Id = image.Id
185
+			var out APIImages
186
+			out.ID = image.ID
187 187
 			out.Created = image.Created.Unix()
188 188
 			outs = append(outs, out)
189 189
 		}
... ...
@@ -191,7 +189,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
191 191
 	return outs, nil
192 192
 }
193 193
 
194
-func (srv *Server) DockerInfo() *ApiInfo {
194
+func (srv *Server) DockerInfo() *APIInfo {
195 195
 	images, _ := srv.runtime.graph.All()
196 196
 	var imgcount int
197 197
 	if images == nil {
... ...
@@ -199,7 +197,7 @@ func (srv *Server) DockerInfo() *ApiInfo {
199 199
 	} else {
200 200
 		imgcount = len(images)
201 201
 	}
202
-	return &ApiInfo{
202
+	return &APIInfo{
203 203
 		Containers:  len(srv.runtime.List()),
204 204
 		Images:      imgcount,
205 205
 		MemoryLimit: srv.runtime.capabilities.MemoryLimit,
... ...
@@ -210,16 +208,16 @@ func (srv *Server) DockerInfo() *ApiInfo {
210 210
 	}
211 211
 }
212 212
 
213
-func (srv *Server) ImageHistory(name string) ([]ApiHistory, error) {
213
+func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
214 214
 	image, err := srv.runtime.repositories.LookupImage(name)
215 215
 	if err != nil {
216 216
 		return nil, err
217 217
 	}
218 218
 
219
-	var outs []ApiHistory = []ApiHistory{} //produce [] when empty instead of 'null'
219
+	outs := []APIHistory{} //produce [] when empty instead of 'null'
220 220
 	err = image.WalkHistory(func(img *Image) error {
221
-		var out ApiHistory
222
-		out.Id = srv.runtime.repositories.ImageName(img.ShortId())
221
+		var out APIHistory
222
+		out.ID = srv.runtime.repositories.ImageName(img.ShortID())
223 223
 		out.Created = img.Created.Unix()
224 224
 		out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
225 225
 		outs = append(outs, out)
... ...
@@ -236,17 +234,17 @@ func (srv *Server) ContainerChanges(name string) ([]Change, error) {
236 236
 	return nil, fmt.Errorf("No such container: %s", name)
237 237
 }
238 238
 
239
-func (srv *Server) Containers(all bool, n int, since, before string) []ApiContainers {
239
+func (srv *Server) Containers(all bool, n int, since, before string) []APIContainers {
240 240
 	var foundBefore bool
241 241
 	var displayed int
242
-	retContainers := []ApiContainers{}
242
+	retContainers := []APIContainers{}
243 243
 
244 244
 	for _, container := range srv.runtime.List() {
245 245
 		if !container.State.Running && !all && n == -1 && since == "" && before == "" {
246 246
 			continue
247 247
 		}
248 248
 		if before != "" {
249
-			if container.ShortId() == before {
249
+			if container.ShortID() == before {
250 250
 				foundBefore = true
251 251
 				continue
252 252
 			}
... ...
@@ -257,13 +255,13 @@ func (srv *Server) Containers(all bool, n int, since, before string) []ApiContai
257 257
 		if displayed == n {
258 258
 			break
259 259
 		}
260
-		if container.ShortId() == since {
260
+		if container.ShortID() == since {
261 261
 			break
262 262
 		}
263 263
 		displayed++
264 264
 
265
-		c := ApiContainers{
266
-			Id: container.Id,
265
+		c := APIContainers{
266
+			ID: container.ID,
267 267
 		}
268 268
 		c.Image = srv.runtime.repositories.ImageName(container.Image)
269 269
 		c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
... ...
@@ -284,7 +282,7 @@ func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, conf
284 284
 	if err != nil {
285 285
 		return "", err
286 286
 	}
287
-	return img.ShortId(), err
287
+	return img.ShortID(), err
288 288
 }
289 289
 
290 290
 func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
... ...
@@ -305,22 +303,23 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
305 305
 	for _, id := range history {
306 306
 		if !srv.runtime.graph.Exists(id) {
307 307
 			out.Write(sf.FormatStatus("Pulling %s metadata", id))
308
-			imgJson, err := r.GetRemoteImageJson(id, endpoint, token)
308
+			imgJSON, err := r.GetRemoteImageJSON(id, endpoint, token)
309 309
 			if err != nil {
310 310
 				// FIXME: Keep goging in case of error?
311 311
 				return err
312 312
 			}
313
-			img, err := NewImgJson(imgJson)
313
+			img, err := NewImgJSON(imgJSON)
314 314
 			if err != nil {
315 315
 				return fmt.Errorf("Failed to parse json: %s", err)
316 316
 			}
317 317
 
318 318
 			// Get the layer
319 319
 			out.Write(sf.FormatStatus("Pulling %s fs layer", id))
320
-			layer, contentLength, err := r.GetRemoteImageLayer(img.Id, endpoint, token)
320
+			layer, contentLength, err := r.GetRemoteImageLayer(img.ID, endpoint, token)
321 321
 			if err != nil {
322 322
 				return err
323 323
 			}
324
+			defer layer.Close()
324 325
 			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), false, img); err != nil {
325 326
 				return err
326 327
 			}
... ...
@@ -329,8 +328,8 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
329 329
 	return nil
330 330
 }
331 331
 
332
-func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, sf *utils.StreamFormatter) error {
333
-	out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress()))
332
+func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, local, remote, askedTag string, sf *utils.StreamFormatter) error {
333
+	out.Write(sf.FormatStatus("Pulling repository %s from %s", local, auth.IndexServerAddress()))
334 334
 	repoData, err := r.GetRepositoryData(remote)
335 335
 	if err != nil {
336 336
 		return err
... ...
@@ -355,22 +354,22 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
355 355
 		}
356 356
 	} else {
357 357
 		// Otherwise, check that the tag exists and use only that one
358
-		if id, exists := tagsList[askedTag]; !exists {
359
-			return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, remote)
360
-		} else {
361
-			repoData.ImgList[id].Tag = askedTag
358
+		id, exists := tagsList[askedTag]
359
+		if !exists {
360
+			return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, local)
362 361
 		}
362
+		repoData.ImgList[id].Tag = askedTag
363 363
 	}
364 364
 
365 365
 	for _, img := range repoData.ImgList {
366 366
 		if askedTag != "" && img.Tag != askedTag {
367
-			utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.Id)
367
+			utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.ID)
368 368
 			continue
369 369
 		}
370
-		out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", img.Id, img.Tag, remote))
370
+		out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", img.ID, img.Tag, remote))
371 371
 		success := false
372 372
 		for _, ep := range repoData.Endpoints {
373
-			if err := srv.pullImage(r, out, img.Id, "https://"+ep+"/v1", repoData.Tokens, sf); err != nil {
373
+			if err := srv.pullImage(r, out, img.ID, "https://"+ep+"/v1", repoData.Tokens, sf); err != nil {
374 374
 				out.Write(sf.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint", askedTag, err))
375 375
 				continue
376 376
 			}
... ...
@@ -385,7 +384,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
385 385
 		if askedTag != "" && tag != askedTag {
386 386
 			continue
387 387
 		}
388
-		if err := srv.runtime.repositories.Set(remote, tag, id, true); err != nil {
388
+		if err := srv.runtime.repositories.Set(local, tag, id, true); err != nil {
389 389
 			return err
390 390
 		}
391 391
 	}
... ...
@@ -405,8 +404,12 @@ func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *util
405 405
 		}
406 406
 		return nil
407 407
 	}
408
-
409
-	if err := srv.pullRepository(r, out, name, tag, sf); err != nil {
408
+	remote := name
409
+	parts := strings.Split(name, "/")
410
+	if len(parts) > 2 {
411
+		remote = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/")))
412
+	}
413
+	if err := srv.pullRepository(r, out, name, remote, tag, sf); err != nil {
410 414
 		return err
411 415
 	}
412 416
 
... ...
@@ -460,16 +463,16 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat
460 460
 			return nil, err
461 461
 		}
462 462
 		img.WalkHistory(func(img *Image) error {
463
-			if _, exists := imageSet[img.Id]; exists {
463
+			if _, exists := imageSet[img.ID]; exists {
464 464
 				return nil
465 465
 			}
466
-			imageSet[img.Id] = struct{}{}
467
-			checksum, err := srv.getChecksum(img.Id)
466
+			imageSet[img.ID] = struct{}{}
467
+			checksum, err := srv.getChecksum(img.ID)
468 468
 			if err != nil {
469 469
 				return err
470 470
 			}
471 471
 			imgList = append([]*registry.ImgData{{
472
-				Id:       img.Id,
472
+				ID:       img.ID,
473 473
 				Checksum: checksum,
474 474
 				Tag:      tag,
475 475
 			}}, imgList...)
... ...
@@ -488,7 +491,13 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
488 488
 	}
489 489
 	out.Write(sf.FormatStatus("Sending image list"))
490 490
 
491
-	repoData, err := r.PushImageJsonIndex(name, imgList, false)
491
+	srvName := name
492
+	parts := strings.Split(name, "/")
493
+	if len(parts) > 2 {
494
+		srvName = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/")))
495
+	}
496
+
497
+	repoData, err := r.PushImageJSONIndex(srvName, imgList, false)
492 498
 	if err != nil {
493 499
 		return err
494 500
 	}
... ...
@@ -497,22 +506,22 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
497 497
 		out.Write(sf.FormatStatus("Pushing repository %s to %s (%d tags)", name, ep, len(localRepo)))
498 498
 		// For each image within the repo, push them
499 499
 		for _, elem := range imgList {
500
-			if _, exists := repoData.ImgList[elem.Id]; exists {
500
+			if _, exists := repoData.ImgList[elem.ID]; exists {
501 501
 				out.Write(sf.FormatStatus("Image %s already on registry, skipping", name))
502 502
 				continue
503 503
 			}
504
-			if err := srv.pushImage(r, out, name, elem.Id, ep, repoData.Tokens, sf); err != nil {
504
+			if err := srv.pushImage(r, out, name, elem.ID, ep, repoData.Tokens, sf); err != nil {
505 505
 				// FIXME: Continue on error?
506 506
 				return err
507 507
 			}
508
-			out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.Id, ep+"/users/"+name+"/"+elem.Tag))
509
-			if err := r.PushRegistryTag(name, elem.Id, elem.Tag, ep, repoData.Tokens); err != nil {
508
+			out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+srvName+"/"+elem.Tag))
509
+			if err := r.PushRegistryTag(srvName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
510 510
 				return err
511 511
 			}
512 512
 		}
513 513
 	}
514 514
 
515
-	if _, err := r.PushImageJsonIndex(name, imgList, true); err != nil {
515
+	if _, err := r.PushImageJSONIndex(srvName, imgList, true); err != nil {
516 516
 		return err
517 517
 	}
518 518
 	return nil
... ...
@@ -532,14 +541,14 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
532 532
 		return err
533 533
 	}
534 534
 	imgData := &registry.ImgData{
535
-		Id:       imgId,
535
+		ID:       imgId,
536 536
 		Checksum: checksum,
537 537
 	}
538 538
 
539 539
 	// Send the json
540
-	if err := r.PushImageJsonRegistry(imgData, jsonRaw, ep, token); err != nil {
540
+	if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep, token); err != nil {
541 541
 		if err == registry.ErrAlreadyExists {
542
-			out.Write(sf.FormatStatus("Image %s already uploaded ; skipping", imgData.Id))
542
+			out.Write(sf.FormatStatus("Image %s already uploaded ; skipping", imgData.ID))
543 543
 			return nil
544 544
 		}
545 545
 		return err
... ...
@@ -572,7 +581,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
572 572
 	}
573 573
 
574 574
 	// Send the layer
575
-	if err := r.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("", "%v/%v (%v)"), sf), ep, token); err != nil {
575
+	if err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("Pushing", "%v/%v (%v)"), sf), ep, token); err != nil {
576 576
 		return err
577 577
 	}
578 578
 	return nil
... ...
@@ -596,7 +605,7 @@ func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.Str
596 596
 		return err
597 597
 	}
598 598
 	out.Write(sf.FormatStatus("The push refers to an image: [%s]", name))
599
-	if err := srv.pushImage(r, out, name, img.Id, endpoint, nil, sf); err != nil {
599
+	if err := srv.pushImage(r, out, name, img.ID, endpoint, nil, sf); err != nil {
600 600
 		return err
601 601
 	}
602 602
 	return nil
... ...
@@ -633,11 +642,11 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
633 633
 	}
634 634
 	// Optionally register the image at REPO/TAG
635 635
 	if repo != "" {
636
-		if err := srv.runtime.repositories.Set(repo, tag, img.Id, true); err != nil {
636
+		if err := srv.runtime.repositories.Set(repo, tag, img.ID, true); err != nil {
637 637
 			return err
638 638
 		}
639 639
 	}
640
-	out.Write(sf.FormatStatus(img.ShortId()))
640
+	out.Write(sf.FormatStatus(img.ShortID()))
641 641
 	return nil
642 642
 }
643 643
 
... ...
@@ -658,7 +667,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
658 658
 		}
659 659
 		return "", err
660 660
 	}
661
-	return container.ShortId(), nil
661
+	return container.ShortID(), nil
662 662
 }
663 663
 
664 664
 func (srv *Server) ContainerRestart(name string, t int) error {
... ...
@@ -695,7 +704,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
695 695
 			for volumeId := range volumes {
696 696
 				// If the requested volu
697 697
 				if c, exists := usedVolumes[volumeId]; exists {
698
-					log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.Id)
698
+					log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.ID)
699 699
 					continue
700 700
 				}
701 701
 				if err := srv.runtime.volumes.Delete(volumeId); err != nil {
... ...
@@ -709,18 +718,112 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
709 709
 	return nil
710 710
 }
711 711
 
712
-func (srv *Server) ImageDelete(name string) error {
713
-	img, err := srv.runtime.repositories.LookupImage(name)
712
+var ErrImageReferenced = errors.New("Image referenced by a repository")
713
+
714
+func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error {
715
+	// If the image is referenced by a repo, do not delete
716
+	if len(srv.runtime.repositories.ByID()[id]) != 0 {
717
+		return ErrImageReferenced
718
+	}
719
+
720
+	// If the image is not referenced but has children, go recursive
721
+	referenced := false
722
+	byParents, err := srv.runtime.graph.ByParent()
714 723
 	if err != nil {
715
-		return fmt.Errorf("No such image: %s", name)
716
-	} else {
717
-		if err := srv.runtime.graph.Delete(img.Id); err != nil {
718
-			return fmt.Errorf("Error deleting image %s: %s", name, err.Error())
724
+		return err
725
+	}
726
+	for _, img := range byParents[id] {
727
+		if err := srv.deleteImageAndChildren(img.ID, imgs); err != nil {
728
+			if err != ErrImageReferenced {
729
+				return err
730
+			}
731
+			referenced = true
732
+		}
733
+	}
734
+	if referenced {
735
+		return ErrImageReferenced
736
+	}
737
+
738
+	// If the image is not referenced and has no children, remove it
739
+	byParents, err = srv.runtime.graph.ByParent()
740
+	if err != nil {
741
+		return err
742
+	}
743
+	if len(byParents[id]) == 0 {
744
+		if err := srv.runtime.repositories.DeleteAll(id); err != nil {
745
+			return err
746
+		}
747
+		err := srv.runtime.graph.Delete(id)
748
+		if err != nil {
749
+			return err
750
+		}
751
+		*imgs = append(*imgs, APIRmi{Deleted: utils.TruncateID(id)})
752
+		return nil
753
+	}
754
+	return nil
755
+}
756
+
757
+func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error {
758
+	if img.Parent != "" {
759
+		parent, err := srv.runtime.graph.Get(img.Parent)
760
+		if err != nil {
761
+			return err
762
+		}
763
+		// Remove all children images
764
+		if err := srv.deleteImageAndChildren(img.Parent, imgs); err != nil {
765
+			return err
719 766
 		}
767
+		return srv.deleteImageParents(parent, imgs)
720 768
 	}
721 769
 	return nil
722 770
 }
723 771
 
772
+func (srv *Server) deleteImage(img *Image, repoName, tag string) (*[]APIRmi, error) {
773
+	//Untag the current image
774
+	var imgs []APIRmi
775
+	tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag)
776
+	if err != nil {
777
+		return nil, err
778
+	}
779
+	if tagDeleted {
780
+		imgs = append(imgs, APIRmi{Untagged: img.ShortID()})
781
+	}
782
+	if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
783
+		if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil {
784
+			if err != ErrImageReferenced {
785
+				return &imgs, err
786
+			}
787
+		} else if err := srv.deleteImageParents(img, &imgs); err != nil {
788
+			if err != ErrImageReferenced {
789
+				return &imgs, err
790
+			}
791
+		}
792
+	}
793
+	return &imgs, nil
794
+}
795
+
796
+func (srv *Server) ImageDelete(name string, autoPrune bool) (*[]APIRmi, error) {
797
+	img, err := srv.runtime.repositories.LookupImage(name)
798
+	if err != nil {
799
+		return nil, fmt.Errorf("No such image: %s", name)
800
+	}
801
+	if !autoPrune {
802
+		if err := srv.runtime.graph.Delete(img.ID); err != nil {
803
+			return nil, fmt.Errorf("Error deleting image %s: %s", name, err.Error())
804
+		}
805
+		return nil, nil
806
+	}
807
+
808
+	var tag string
809
+	if strings.Contains(name, ":") {
810
+		nameParts := strings.Split(name, ":")
811
+		name = nameParts[0]
812
+		tag = nameParts[1]
813
+	}
814
+
815
+	return srv.deleteImage(img, name, tag)
816
+}
817
+
724 818
 func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error) {
725 819
 
726 820
 	// Retrieve all images
... ...
@@ -735,7 +838,7 @@ func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error)
735 735
 		if _, exists := imageMap[img.Parent]; !exists {
736 736
 			imageMap[img.Parent] = make(map[string]struct{})
737 737
 		}
738
-		imageMap[img.Parent][img.Id] = struct{}{}
738
+		imageMap[img.Parent][img.ID] = struct{}{}
739 739
 	}
740 740
 
741 741
 	// Loop on the children of the given image and check the config
... ...
@@ -869,7 +972,7 @@ func (srv *Server) ImageInspect(name string) (*Image, error) {
869 869
 	return nil, fmt.Errorf("No such image: %s", name)
870 870
 }
871 871
 
872
-func NewServer(autoRestart bool) (*Server, error) {
872
+func NewServer(autoRestart, enableCors bool) (*Server, error) {
873 873
 	if runtime.GOARCH != "amd64" {
874 874
 		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
875 875
 	}
... ...
@@ -878,12 +981,14 @@ func NewServer(autoRestart bool) (*Server, error) {
878 878
 		return nil, err
879 879
 	}
880 880
 	srv := &Server{
881
-		runtime: runtime,
881
+		runtime:    runtime,
882
+		enableCors: enableCors,
882 883
 	}
883 884
 	runtime.srv = srv
884 885
 	return srv, nil
885 886
 }
886 887
 
887 888
 type Server struct {
888
-	runtime *Runtime
889
+	runtime    *Runtime
890
+	enableCors bool
889 891
 }
... ...
@@ -4,6 +4,58 @@ import (
4 4
 	"testing"
5 5
 )
6 6
 
7
+func TestContainerTagImageDelete(t *testing.T) {
8
+	runtime, err := newTestRuntime()
9
+	if err != nil {
10
+		t.Fatal(err)
11
+	}
12
+	defer nuke(runtime)
13
+
14
+	srv := &Server{runtime: runtime}
15
+
16
+	if err := srv.runtime.repositories.Set("utest", "tag1", unitTestImageName, false); err != nil {
17
+		t.Fatal(err)
18
+	}
19
+	if err := srv.runtime.repositories.Set("utest/docker", "tag2", unitTestImageName, false); err != nil {
20
+		t.Fatal(err)
21
+	}
22
+
23
+	images, err := srv.Images(false, "")
24
+	if err != nil {
25
+		t.Fatal(err)
26
+	}
27
+
28
+	if len(images) != 3 {
29
+		t.Errorf("Excepted 3 images, %d found", len(images))
30
+	}
31
+
32
+	if _, err := srv.ImageDelete("utest/docker:tag2", true); err != nil {
33
+		t.Fatal(err)
34
+	}
35
+
36
+	images, err = srv.Images(false, "")
37
+	if err != nil {
38
+		t.Fatal(err)
39
+	}
40
+
41
+	if len(images) != 2 {
42
+		t.Errorf("Excepted 2 images, %d found", len(images))
43
+	}
44
+
45
+	if _, err := srv.ImageDelete("utest:tag1", true); err != nil {
46
+		t.Fatal(err)
47
+	}
48
+
49
+	images, err = srv.Images(false, "")
50
+	if err != nil {
51
+		t.Fatal(err)
52
+	}
53
+
54
+	if len(images) != 1 {
55
+		t.Errorf("Excepted 1 image, %d found", len(images))
56
+	}
57
+}
58
+
7 59
 func TestCreateRm(t *testing.T) {
8 60
 	runtime, err := newTestRuntime()
9 61
 	if err != nil {
... ...
@@ -13,7 +65,7 @@ func TestCreateRm(t *testing.T) {
13 13
 
14 14
 	srv := &Server{runtime: runtime}
15 15
 
16
-	config, _, err := ParseRun([]string{GetTestImage(runtime).Id, "echo test"}, nil)
16
+	config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
17 17
 	if err != nil {
18 18
 		t.Fatal(err)
19 19
 	}
... ...
@@ -46,7 +98,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
46 46
 
47 47
 	srv := &Server{runtime: runtime}
48 48
 
49
-	config, _, err := ParseRun([]string{GetTestImage(runtime).Id, "/bin/cat"}, nil)
49
+	config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "/bin/cat"}, nil)
50 50
 	if err != nil {
51 51
 		t.Fatal(err)
52 52
 	}
... ...
@@ -11,7 +11,7 @@ import (
11 11
 	"strings"
12 12
 )
13 13
 
14
-const DEFAULT_TAG = "latest"
14
+const DEFAULTTAG = "latest"
15 15
 
16 16
 type TagStore struct {
17 17
 	path         string
... ...
@@ -72,7 +72,7 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
72 72
 		// (so we can pass all errors here)
73 73
 		repoAndTag := strings.SplitN(name, ":", 2)
74 74
 		if len(repoAndTag) == 1 {
75
-			repoAndTag = append(repoAndTag, DEFAULT_TAG)
75
+			repoAndTag = append(repoAndTag, DEFAULTTAG)
76 76
 		}
77 77
 		if i, err := store.GetImage(repoAndTag[0], repoAndTag[1]); err != nil {
78 78
 			return nil, err
... ...
@@ -87,27 +87,73 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
87 87
 
88 88
 // Return a reverse-lookup table of all the names which refer to each image
89 89
 // Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
90
-func (store *TagStore) ById() map[string][]string {
91
-	byId := make(map[string][]string)
90
+func (store *TagStore) ByID() map[string][]string {
91
+	byID := make(map[string][]string)
92 92
 	for repoName, repository := range store.Repositories {
93 93
 		for tag, id := range repository {
94 94
 			name := repoName + ":" + tag
95
-			if _, exists := byId[id]; !exists {
96
-				byId[id] = []string{name}
95
+			if _, exists := byID[id]; !exists {
96
+				byID[id] = []string{name}
97 97
 			} else {
98
-				byId[id] = append(byId[id], name)
99
-				sort.Strings(byId[id])
98
+				byID[id] = append(byID[id], name)
99
+				sort.Strings(byID[id])
100 100
 			}
101 101
 		}
102 102
 	}
103
-	return byId
103
+	return byID
104 104
 }
105 105
 
106 106
 func (store *TagStore) ImageName(id string) string {
107
-	if names, exists := store.ById()[id]; exists && len(names) > 0 {
107
+	if names, exists := store.ByID()[id]; exists && len(names) > 0 {
108 108
 		return names[0]
109 109
 	}
110
-	return utils.TruncateId(id)
110
+	return utils.TruncateID(id)
111
+}
112
+
113
+func (store *TagStore) DeleteAll(id string) error {
114
+	names, exists := store.ByID()[id]
115
+	if !exists || len(names) == 0 {
116
+		return nil
117
+	}
118
+	for _, name := range names {
119
+		if strings.Contains(name, ":") {
120
+			nameParts := strings.Split(name, ":")
121
+			if _, err := store.Delete(nameParts[0], nameParts[1]); err != nil {
122
+				return err
123
+			}
124
+		} else {
125
+			if _, err := store.Delete(name, ""); err != nil {
126
+				return err
127
+			}
128
+		}
129
+	}
130
+	return nil
131
+}
132
+
133
+func (store *TagStore) Delete(repoName, tag string) (bool, error) {
134
+	deleted := false
135
+	if err := store.Reload(); err != nil {
136
+		return false, err
137
+	}
138
+	if r, exists := store.Repositories[repoName]; exists {
139
+		if tag != "" {
140
+			if _, exists2 := r[tag]; exists2 {
141
+				delete(r, tag)
142
+				if len(r) == 0 {
143
+					delete(store.Repositories, repoName)
144
+				}
145
+				deleted = true
146
+			} else {
147
+				return false, fmt.Errorf("No such tag: %s:%s", repoName, tag)
148
+			}
149
+		} else {
150
+			delete(store.Repositories, repoName)
151
+			deleted = true
152
+		}
153
+	} else {
154
+		fmt.Errorf("No such repository: %s", repoName)
155
+	}
156
+	return deleted, store.Save()
111 157
 }
112 158
 
113 159
 func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
... ...
@@ -116,7 +162,7 @@ func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
116 116
 		return err
117 117
 	}
118 118
 	if tag == "" {
119
-		tag = DEFAULT_TAG
119
+		tag = DEFAULTTAG
120 120
 	}
121 121
 	if err := validateRepoName(repoName); err != nil {
122 122
 		return err
... ...
@@ -133,11 +179,11 @@ func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
133 133
 	} else {
134 134
 		repo = make(map[string]string)
135 135
 		if old, exists := store.Repositories[repoName]; exists && !force {
136
-			return fmt.Errorf("Tag %s:%s is already set to %s", repoName, tag, old)
136
+			return fmt.Errorf("Conflict: Tag %s:%s is already set to %s", repoName, tag, old)
137 137
 		}
138 138
 		store.Repositories[repoName] = repo
139 139
 	}
140
-	repo[tag] = img.Id
140
+	repo[tag] = img.ID
141 141
 	return store.Save()
142 142
 }
143 143
 
... ...
@@ -34,7 +34,7 @@ func Go(f func() error) chan error {
34 34
 // Request a given URL and return an io.Reader
35 35
 func Download(url string, stderr io.Writer) (*http.Response, error) {
36 36
 	var resp *http.Response
37
-	var err error = nil
37
+	var err error
38 38
 	if resp, err = http.Get(url); err != nil {
39 39
 		return nil, err
40 40
 	}
... ...
@@ -349,11 +349,11 @@ func (idx *TruncIndex) Get(s string) (string, error) {
349 349
 	return string(idx.bytes[before:after]), err
350 350
 }
351 351
 
352
-// TruncateId returns a shorthand version of a string identifier for convenience.
352
+// TruncateID returns a shorthand version of a string identifier for convenience.
353 353
 // A collision with other shorthands is very unlikely, but possible.
354 354
 // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
355 355
 // will need to use a langer prefix, or the full-length Id.
356
-func TruncateId(id string) string {
356
+func TruncateID(id string) string {
357 357
 	shortLen := 12
358 358
 	if len(id) < shortLen {
359 359
 		shortLen = len(id)
... ...
@@ -566,7 +566,7 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
566 566
 	return &WriteFlusher{w: w, flusher: flusher}
567 567
 }
568 568
 
569
-type JsonMessage struct {
569
+type JSONMessage struct {
570 570
 	Status   string `json:"status,omitempty"`
571 571
 	Progress string `json:"progress,omitempty"`
572 572
 	Error    string `json:"error,omitempty"`
... ...
@@ -585,7 +585,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte
585 585
 	sf.used = true
586 586
 	str := fmt.Sprintf(format, a...)
587 587
 	if sf.json {
588
-		b, err := json.Marshal(&JsonMessage{Status:str});
588
+		b, err := json.Marshal(&JSONMessage{Status:str});
589 589
 		if err != nil {
590 590
 			return sf.FormatError(err)
591 591
 		}
... ...
@@ -597,7 +597,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte
597 597
 func (sf *StreamFormatter) FormatError(err error) []byte {
598 598
 	sf.used = true
599 599
 	if sf.json {
600
-		if b, err := json.Marshal(&JsonMessage{Error:err.Error()}); err == nil {
600
+		if b, err := json.Marshal(&JSONMessage{Error:err.Error()}); err == nil {
601 601
 			return b
602 602
 		}
603 603
 		return []byte("{\"error\":\"format error\"}")
... ...
@@ -608,7 +608,7 @@ func (sf *StreamFormatter) FormatError(err error) []byte {
608 608
 func (sf *StreamFormatter) FormatProgress(action, str string) []byte {
609 609
 	sf.used = true
610 610
 	if sf.json {
611
-		b, err := json.Marshal(&JsonMessage{Progress:str})
611
+		b, err := json.Marshal(&JSONMessage{Status: action, Progress:str})
612 612
 		if err != nil {
613 613
                         return nil
614 614
                 }