Browse code

Merge branch 'master' into 22-add_sizes_images_and_containers-feature

Victor Vieux authored on 2013/06/14 19:05:06
Showing 52 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 APIVERSION = 1.1
16
+const APIVERSION = 1.2
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 {
... ...
@@ -69,8 +71,10 @@ func getBoolParam(value string) (bool, error) {
69 69
 }
70 70
 
71 71
 func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
72
-	// FIXME: Handle multiple login at once
73
-	// FIXME: return specific error code if config file missing?
72
+	if version > 1.1 {
73
+		w.WriteHeader(http.StatusNotFound)
74
+		return nil
75
+	}
74 76
 	authConfig, err := auth.LoadConfig(srv.runtime.root)
75 77
 	if err != nil {
76 78
 		if err != auth.ErrConfigFileMissing {
... ...
@@ -87,29 +91,34 @@ func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
87 87
 }
88 88
 
89 89
 func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
90
-	// FIXME: Handle multiple login at once
91
-	config := &auth.AuthConfig{}
92
-	if err := json.NewDecoder(r.Body).Decode(config); err != nil {
90
+	authConfig := &auth.AuthConfig{}
91
+	err := json.NewDecoder(r.Body).Decode(authConfig)
92
+	if err != nil {
93 93
 		return err
94 94
 	}
95
-
96
-	authConfig, err := auth.LoadConfig(srv.runtime.root)
97
-	if err != nil {
98
-		if err != auth.ErrConfigFileMissing {
95
+	status := ""
96
+	if version > 1.1 {
97
+		status, err = auth.Login(authConfig, false)
98
+		if err != nil {
99 99
 			return err
100 100
 		}
101
-		authConfig = &auth.AuthConfig{}
102
-	}
103
-	if config.Username == authConfig.Username {
104
-		config.Password = authConfig.Password
105
-	}
101
+	} else {
102
+		localAuthConfig, err := auth.LoadConfig(srv.runtime.root)
103
+		if err != nil {
104
+			if err != auth.ErrConfigFileMissing {
105
+				return err
106
+			}
107
+		}
108
+		if authConfig.Username == localAuthConfig.Username {
109
+			authConfig.Password = localAuthConfig.Password
110
+		}
106 111
 
107
-	newAuthConfig := auth.NewAuthConfig(config.Username, config.Password, config.Email, srv.runtime.root)
108
-	status, err := auth.Login(newAuthConfig)
109
-	if err != nil {
110
-		return err
112
+		newAuthConfig := auth.NewAuthConfig(authConfig.Username, authConfig.Password, authConfig.Email, srv.runtime.root)
113
+		status, err = auth.Login(newAuthConfig, true)
114
+		if err != nil {
115
+			return err
116
+		}
111 117
 	}
112
-
113 118
 	if status != "" {
114 119
 		b, err := json.Marshal(&APIAuth{Status: status})
115 120
 		if err != nil {
... ...
@@ -320,7 +329,7 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht
320 320
 	sf := utils.NewStreamFormatter(version > 1.0)
321 321
 	if image != "" { //pull
322 322
 		registry := r.Form.Get("registry")
323
-		if err := srv.ImagePull(image, tag, registry, w, sf); err != nil {
323
+		if err := srv.ImagePull(image, tag, registry, w, sf, &auth.AuthConfig{}); err != nil {
324 324
 			if sf.Used() {
325 325
 				w.Write(sf.FormatError(err))
326 326
 				return nil
... ...
@@ -388,6 +397,18 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
388 388
 }
389 389
 
390 390
 func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
391
+	authConfig := &auth.AuthConfig{}
392
+	if version > 1.1 {
393
+		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
394
+			return err
395
+		}
396
+	} else {
397
+		localAuthConfig, err := auth.LoadConfig(srv.runtime.root)
398
+		if err != nil && err != auth.ErrConfigFileMissing {
399
+			return err
400
+		}
401
+		authConfig = localAuthConfig
402
+	}
391 403
 	if err := parseForm(r); err != nil {
392 404
 		return err
393 405
 	}
... ...
@@ -401,7 +422,7 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
401 401
 		w.Header().Set("Content-Type", "application/json")
402 402
 	}
403 403
 	sf := utils.NewStreamFormatter(version > 1.0)
404
-	if err := srv.ImagePush(name, registry, w, sf); err != nil {
404
+	if err := srv.ImagePush(name, registry, w, sf, authConfig); err != nil {
405 405
 		if sf.Used() {
406 406
 			w.Write(sf.FormatError(err))
407 407
 			return nil
... ...
@@ -481,14 +502,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.1)
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
 
... ...
@@ -703,9 +740,18 @@ 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": {
... ...
@@ -745,6 +791,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 {
... ...
@@ -769,6 +818,9 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
769 769
 				if err != nil {
770 770
 					version = APIVERSION
771 771
 				}
772
+				if srv.enableCors {
773
+					writeCorsHeaders(w, r)
774
+				}
772 775
 				if version == 0 || version > APIVERSION {
773 776
 					w.WriteHeader(http.StatusNotFound)
774 777
 					return
... ...
@@ -777,9 +829,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
 }
... ...
@@ -25,6 +25,11 @@ type APIInfo struct {
25 25
 	SwapLimit   bool `json:",omitempty"`
26 26
 }
27 27
 
28
+type APIRmi struct {
29
+	Deleted  string `json:",omitempty"`
30
+	Untagged string `json:",omitempty"`
31
+}
32
+
28 33
 type APIContainers struct {
29 34
 	ID         string `json:"Id"`
30 35
 	Image      string
... ...
@@ -6,7 +6,6 @@ import (
6 6
 	"bytes"
7 7
 	"encoding/json"
8 8
 	"github.com/dotcloud/docker/auth"
9
-	"github.com/dotcloud/docker/registry"
10 9
 	"github.com/dotcloud/docker/utils"
11 10
 	"io"
12 11
 	"net"
... ...
@@ -18,7 +17,7 @@ import (
18 18
 	"time"
19 19
 )
20 20
 
21
-func TestGetAuth(t *testing.T) {
21
+func TestPostAuth(t *testing.T) {
22 22
 	runtime, err := newTestRuntime()
23 23
 	if err != nil {
24 24
 		t.Fatal(err)
... ...
@@ -54,12 +53,6 @@ func TestGetAuth(t *testing.T) {
54 54
 	if r.Code != http.StatusOK && r.Code != 0 {
55 55
 		t.Fatalf("%d OK or 0 expected, received %d\n", http.StatusOK, r.Code)
56 56
 	}
57
-
58
-	newAuthConfig := registry.NewRegistry(runtime.root).GetAuthConfig(false)
59
-	if newAuthConfig.Username != authConfig.Username ||
60
-		newAuthConfig.Email != authConfig.Email {
61
-		t.Fatalf("The auth configuration hasn't been set correctly")
62
-	}
63 57
 }
64 58
 
65 59
 func TestGetVersion(t *testing.T) {
... ...
@@ -494,40 +487,6 @@ func TestGetContainersByName(t *testing.T) {
494 494
 	}
495 495
 }
496 496
 
497
-func TestPostAuth(t *testing.T) {
498
-	runtime, err := newTestRuntime()
499
-	if err != nil {
500
-		t.Fatal(err)
501
-	}
502
-	defer nuke(runtime)
503
-
504
-	srv := &Server{
505
-		runtime: runtime,
506
-	}
507
-
508
-	config := &auth.AuthConfig{
509
-		Username: "utest",
510
-		Email:    "utest@yopmail.com",
511
-	}
512
-
513
-	authStr := auth.EncodeAuth(config)
514
-	auth.SaveConfig(runtime.root, authStr, config.Email)
515
-
516
-	r := httptest.NewRecorder()
517
-	if err := getAuth(srv, APIVERSION, r, nil, nil); err != nil {
518
-		t.Fatal(err)
519
-	}
520
-
521
-	authConfig := &auth.AuthConfig{}
522
-	if err := json.Unmarshal(r.Body.Bytes(), authConfig); err != nil {
523
-		t.Fatal(err)
524
-	}
525
-
526
-	if authConfig.Username != config.Username || authConfig.Email != config.Email {
527
-		t.Errorf("The retrieve auth mismatch with the one set.")
528
-	}
529
-}
530
-
531 497
 func TestPostCommit(t *testing.T) {
532 498
 	runtime, err := newTestRuntime()
533 499
 	if err != nil {
... ...
@@ -1239,9 +1198,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
... ...
@@ -48,7 +48,7 @@ func IndexServerAddress() string {
48 48
 }
49 49
 
50 50
 // create a base64 encoded auth string to store in config
51
-func EncodeAuth(authConfig *AuthConfig) string {
51
+func encodeAuth(authConfig *AuthConfig) string {
52 52
 	authStr := authConfig.Username + ":" + authConfig.Password
53 53
 	msg := []byte(authStr)
54 54
 	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
... ...
@@ -57,7 +57,7 @@ func EncodeAuth(authConfig *AuthConfig) string {
57 57
 }
58 58
 
59 59
 // decode the auth string
60
-func DecodeAuth(authStr string) (*AuthConfig, error) {
60
+func decodeAuth(authStr string) (*AuthConfig, error) {
61 61
 	decLen := base64.StdEncoding.DecodedLen(len(authStr))
62 62
 	decoded := make([]byte, decLen)
63 63
 	authByte := []byte(authStr)
... ...
@@ -82,7 +82,7 @@ func DecodeAuth(authStr string) (*AuthConfig, error) {
82 82
 func LoadConfig(rootPath string) (*AuthConfig, error) {
83 83
 	confFile := path.Join(rootPath, CONFIGFILE)
84 84
 	if _, err := os.Stat(confFile); err != nil {
85
-		return nil, ErrConfigFileMissing
85
+		return &AuthConfig{rootPath:rootPath}, ErrConfigFileMissing
86 86
 	}
87 87
 	b, err := ioutil.ReadFile(confFile)
88 88
 	if err != nil {
... ...
@@ -94,7 +94,7 @@ func LoadConfig(rootPath string) (*AuthConfig, error) {
94 94
 	}
95 95
 	origAuth := strings.Split(arr[0], " = ")
96 96
 	origEmail := strings.Split(arr[1], " = ")
97
-	authConfig, err := DecodeAuth(origAuth[1])
97
+	authConfig, err := decodeAuth(origAuth[1])
98 98
 	if err != nil {
99 99
 		return nil, err
100 100
 	}
... ...
@@ -104,13 +104,13 @@ func LoadConfig(rootPath string) (*AuthConfig, error) {
104 104
 }
105 105
 
106 106
 // save the auth config
107
-func SaveConfig(rootPath, authStr string, email string) error {
108
-	confFile := path.Join(rootPath, CONFIGFILE)
109
-	if len(email) == 0 {
107
+func SaveConfig(authConfig *AuthConfig) error {
108
+	confFile := path.Join(authConfig.rootPath, CONFIGFILE)
109
+	if len(authConfig.Email) == 0 {
110 110
 		os.Remove(confFile)
111 111
 		return nil
112 112
 	}
113
-	lines := "auth = " + authStr + "\n" + "email = " + email + "\n"
113
+	lines := "auth = " + encodeAuth(authConfig) + "\n" + "email = " + authConfig.Email + "\n"
114 114
 	b := []byte(lines)
115 115
 	err := ioutil.WriteFile(confFile, b, 0600)
116 116
 	if err != nil {
... ...
@@ -120,7 +120,7 @@ func SaveConfig(rootPath, authStr string, email string) error {
120 120
 }
121 121
 
122 122
 // try to register/login to the registry server
123
-func Login(authConfig *AuthConfig) (string, error) {
123
+func Login(authConfig *AuthConfig, store bool) (string, error) {
124 124
 	storeConfig := false
125 125
 	client := &http.Client{}
126 126
 	reqStatusCode := 0
... ...
@@ -168,8 +168,10 @@ func Login(authConfig *AuthConfig) (string, error) {
168 168
 				status = "Login Succeeded\n"
169 169
 				storeConfig = true
170 170
 			} else if resp.StatusCode == 401 {
171
-				if err := SaveConfig(authConfig.rootPath, "", ""); err != nil {
172
-					return "", err
171
+				if store {
172
+					if err := SaveConfig(authConfig); err != nil {
173
+						return "", err
174
+					}
173 175
 				}
174 176
 				return "", fmt.Errorf("Wrong login/password, please try again")
175 177
 			} else {
... ...
@@ -182,9 +184,8 @@ func Login(authConfig *AuthConfig) (string, error) {
182 182
 	} else {
183 183
 		return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
184 184
 	}
185
-	if storeConfig {
186
-		authStr := EncodeAuth(authConfig)
187
-		if err := SaveConfig(authConfig.rootPath, authStr, authConfig.Email); err != nil {
185
+	if storeConfig && store {
186
+		if err := SaveConfig(authConfig); err != nil {
188 187
 			return "", err
189 188
 		}
190 189
 	}
... ...
@@ -61,7 +61,7 @@ func (b *buildFile) CmdFrom(name string) error {
61 61
 				remote = name
62 62
 			}
63 63
 
64
-			if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false)); err != nil {
64
+			if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false), nil); err != nil {
65 65
 				return err
66 66
 			}
67 67
 
... ...
@@ -176,16 +176,14 @@ func (b *buildFile) CmdAdd(args string) error {
176 176
 	dest := strings.Trim(tmp[1], " ")
177 177
 
178 178
 	cmd := b.config.Cmd
179
-	b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)}
180
-	cid, err := b.run()
179
+
180
+	// Create the container and start it
181
+	container, err := b.builder.Create(b.config)
181 182
 	if err != nil {
182 183
 		return err
183 184
 	}
185
+	b.tmpContainers[container.ID] = struct{}{}
184 186
 
185
-	container := b.runtime.Get(cid)
186
-	if container == nil {
187
-		return fmt.Errorf("Error while creating the container (CmdAdd)")
188
-	}
189 187
 	if err := container.EnsureMounted(); err != nil {
190 188
 		return err
191 189
 	}
... ...
@@ -220,7 +218,7 @@ func (b *buildFile) CmdAdd(args string) error {
220 220
 			return err
221 221
 		}
222 222
 	}
223
-	if err := b.commit(cid, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil {
223
+	if err := b.commit(container.ID, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil {
224 224
 		return err
225 225
 	}
226 226
 	b.config.Cmd = cmd
... ...
@@ -272,11 +270,19 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
272 272
 			utils.Debugf("[BUILDER] Cache miss")
273 273
 		}
274 274
 
275
-		cid, err := b.run()
275
+		// Create the container and start it
276
+		container, err := b.builder.Create(b.config)
276 277
 		if err != nil {
277 278
 			return err
278 279
 		}
279
-		id = cid
280
+		b.tmpContainers[container.ID] = struct{}{}
281
+
282
+		if err := container.EnsureMounted(); err != nil {
283
+			return err
284
+		}
285
+		defer container.Unmount()
286
+
287
+		id = container.ID
280 288
 	}
281 289
 
282 290
 	container := b.runtime.Get(id)
... ...
@@ -313,10 +319,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
 }
... ...
@@ -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
... ...
@@ -284,27 +287,16 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
284 284
 		return nil
285 285
 	}
286 286
 
287
-	body, _, err := cli.call("GET", "/auth", nil)
288
-	if err != nil {
289
-		return err
290
-	}
291
-
292
-	var out auth.AuthConfig
293
-	err = json.Unmarshal(body, &out)
294
-	if err != nil {
295
-		return err
296
-	}
297
-
298 287
 	var username string
299 288
 	var password string
300 289
 	var email string
301 290
 
302
-	fmt.Print("Username (", out.Username, "): ")
291
+	fmt.Print("Username (", cli.authConfig.Username, "): ")
303 292
 	username = readAndEchoString(os.Stdin, os.Stdout)
304 293
 	if username == "" {
305
-		username = out.Username
294
+		username = cli.authConfig.Username
306 295
 	}
307
-	if username != out.Username {
296
+	if username != cli.authConfig.Username {
308 297
 		fmt.Print("Password: ")
309 298
 		password = readString(os.Stdin, os.Stdout)
310 299
 
... ...
@@ -312,20 +304,21 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
312 312
 			return fmt.Errorf("Error : Password Required")
313 313
 		}
314 314
 
315
-		fmt.Print("Email (", out.Email, "): ")
315
+		fmt.Print("Email (", cli.authConfig.Email, "): ")
316 316
 		email = readAndEchoString(os.Stdin, os.Stdout)
317 317
 		if email == "" {
318
-			email = out.Email
318
+			email = cli.authConfig.Email
319 319
 		}
320 320
 	} else {
321
-		email = out.Email
321
+		email = cli.authConfig.Email
322 322
 	}
323
+	term.RestoreTerminal(oldState)
323 324
 
324
-	out.Username = username
325
-	out.Password = password
326
-	out.Email = email
325
+	cli.authConfig.Username = username
326
+	cli.authConfig.Password = password
327
+	cli.authConfig.Email = email
327 328
 
328
-	body, _, err = cli.call("POST", "/auth", out)
329
+	body, _, err := cli.call("POST", "/auth", cli.authConfig)
329 330
 	if err != nil {
330 331
 		return err
331 332
 	}
... ...
@@ -333,10 +326,11 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
333 333
 	var out2 APIAuth
334 334
 	err = json.Unmarshal(body, &out2)
335 335
 	if err != nil {
336
+		auth.LoadConfig(os.Getenv("HOME"))
336 337
 		return err
337 338
 	}
339
+	auth.SaveConfig(cli.authConfig)
338 340
 	if out2.Status != "" {
339
-		term.RestoreTerminal(oldState)
340 341
 		fmt.Print(out2.Status)
341 342
 	}
342 343
 	return nil
... ...
@@ -457,7 +451,7 @@ func (cli *DockerCli) CmdStop(args ...string) error {
457 457
 	for _, name := range cmd.Args() {
458 458
 		_, _, err := cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil)
459 459
 		if err != nil {
460
-			fmt.Printf("%s", err)
460
+			fmt.Fprintf(os.Stderr, "%s", err)
461 461
 		} else {
462 462
 			fmt.Println(name)
463 463
 		}
... ...
@@ -482,7 +476,7 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
482 482
 	for _, name := range cmd.Args() {
483 483
 		_, _, err := cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil)
484 484
 		if err != nil {
485
-			fmt.Printf("%s", err)
485
+			fmt.Fprintf(os.Stderr, "%s", err)
486 486
 		} else {
487 487
 			fmt.Println(name)
488 488
 		}
... ...
@@ -503,7 +497,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
503 503
 	for _, name := range args {
504 504
 		_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
505 505
 		if err != nil {
506
-			fmt.Printf("%s", err)
506
+			fmt.Fprintf(os.Stderr, "%s", err)
507 507
 		} else {
508 508
 			fmt.Println(name)
509 509
 		}
... ...
@@ -512,29 +506,38 @@ func (cli *DockerCli) CmdStart(args ...string) error {
512 512
 }
513 513
 
514 514
 func (cli *DockerCli) CmdInspect(args ...string) error {
515
-	cmd := Subcmd("inspect", "CONTAINER|IMAGE", "Return low-level information on a container/image")
515
+	cmd := Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container/image")
516 516
 	if err := cmd.Parse(args); err != nil {
517 517
 		return nil
518 518
 	}
519
-	if cmd.NArg() != 1 {
519
+	if cmd.NArg() < 1 {
520 520
 		cmd.Usage()
521 521
 		return nil
522 522
 	}
523
-	obj, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
524
-	if err != nil {
525
-		obj, _, err = cli.call("GET", "/images/"+cmd.Arg(0)+"/json", nil)
523
+	fmt.Printf("[")
524
+	for i, name := range args {
525
+		if i > 0 {
526
+			fmt.Printf(",")
527
+		}
528
+		obj, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
526 529
 		if err != nil {
527
-			return err
530
+			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
531
+			if err != nil {
532
+				fmt.Fprintf(os.Stderr, "%s", err)
533
+				continue
534
+			}
528 535
 		}
529
-	}
530 536
 
531
-	indented := new(bytes.Buffer)
532
-	if err = json.Indent(indented, obj, "", "    "); err != nil {
533
-		return err
534
-	}
535
-	if _, err := io.Copy(os.Stdout, indented); err != nil {
536
-		return err
537
+		indented := new(bytes.Buffer)
538
+		if err = json.Indent(indented, obj, "", "    "); err != nil {
539
+			fmt.Fprintf(os.Stderr, "%s", err)
540
+			continue
541
+		}
542
+		if _, err := io.Copy(os.Stdout, indented); err != nil {
543
+			fmt.Fprintf(os.Stderr, "%s", err)
544
+		}
537 545
 	}
546
+	fmt.Printf("]")
538 547
 	return nil
539 548
 }
540 549
 
... ...
@@ -561,7 +564,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
561 561
 	if frontend, exists := out.NetworkSettings.PortMapping[cmd.Arg(1)]; exists {
562 562
 		fmt.Println(frontend)
563 563
 	} else {
564
-		return fmt.Errorf("error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0))
564
+		return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0))
565 565
 	}
566 566
 	return nil
567 567
 }
... ...
@@ -578,11 +581,22 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
578 578
 	}
579 579
 
580 580
 	for _, name := range cmd.Args() {
581
-		_, _, err := cli.call("DELETE", "/images/"+name, nil)
581
+		body, _, err := cli.call("DELETE", "/images/"+name, nil)
582 582
 		if err != nil {
583
-			fmt.Printf("%s", err)
583
+			fmt.Fprintf(os.Stderr, "%s", err)
584 584
 		} else {
585
-			fmt.Println(name)
585
+			var outs []APIRmi
586
+			err = json.Unmarshal(body, &outs)
587
+			if err != nil {
588
+				return err
589
+			}
590
+			for _, out := range outs {
591
+				if out.Deleted != "" {
592
+					fmt.Println("Deleted:", out.Deleted)
593
+				} else {
594
+					fmt.Println("Untagged:", out.Untagged)
595
+				}
596
+			}
586 597
 		}
587 598
 	}
588 599
 	return nil
... ...
@@ -701,18 +715,22 @@ func (cli *DockerCli) CmdPush(args ...string) error {
701 701
 		return nil
702 702
 	}
703 703
 
704
-	username, err := cli.checkIfLogged(*registry == "", "push")
705
-	if err != nil {
704
+	if err := cli.checkIfLogged(*registry == "", "push"); err != nil {
706 705
 		return err
707 706
 	}
708 707
 
709 708
 	if len(strings.SplitN(name, "/", 2)) == 1 {
710
-		return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name)
709
+		return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", cli.authConfig.Username, name)
710
+	}
711
+
712
+	buf, err := json.Marshal(cli.authConfig)
713
+	if err != nil {
714
+		return err
711 715
 	}
712 716
 
713 717
 	v := url.Values{}
714 718
 	v.Set("registry", *registry)
715
-	if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, os.Stdout); err != nil {
719
+	if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), os.Stdout); err != nil {
716 720
 		return err
717 721
 	}
718 722
 	return nil
... ...
@@ -1037,7 +1055,9 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
1037 1037
 		connections += 1
1038 1038
 	}
1039 1039
 	chErrors := make(chan error, connections)
1040
-	cli.monitorTtySize(cmd.Arg(0))
1040
+	if container.Config.Tty {
1041
+		cli.monitorTtySize(cmd.Arg(0))
1042
+	}
1041 1043
 	if splitStderr {
1042 1044
 		go func() {
1043 1045
 			chErrors <- cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?stream=1&stderr=1", false, nil, os.Stderr)
... ...
@@ -1089,7 +1109,12 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
1089 1089
 	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
1090 1090
 	fmt.Fprintf(w, "NAME\tDESCRIPTION\n")
1091 1091
 	for _, out := range outs {
1092
-		fmt.Fprintf(w, "%s\t%s\n", out.Name, out.Description)
1092
+		desc := strings.Replace(out.Description, "\n", " ", -1)
1093
+		desc = strings.Replace(desc, "\r", " ", -1)
1094
+		if len(desc) > 45 {
1095
+			desc = utils.Trunc(desc, 42) + "..."
1096
+		}
1097
+		fmt.Fprintf(w, "%s\t%s\n", out.Name, desc)
1093 1098
 	}
1094 1099
 	w.Flush()
1095 1100
 	return nil
... ...
@@ -1243,7 +1268,9 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1243 1243
 	}
1244 1244
 	if connections > 0 {
1245 1245
 		chErrors := make(chan error, connections)
1246
-		cli.monitorTtySize(out.ID)
1246
+		if config.Tty {
1247
+			cli.monitorTtySize(out.ID)
1248
+		}
1247 1249
 
1248 1250
 		if splitStderr && config.AttachStderr {
1249 1251
 			go func() {
... ...
@@ -1270,6 +1297,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1270 1270
 		for connections > 0 {
1271 1271
 			err := <-chErrors
1272 1272
 			if err != nil {
1273
+				utils.Debugf("Error hijack: %s", err)
1273 1274
 				return err
1274 1275
 			}
1275 1276
 			connections -= 1
... ...
@@ -1278,38 +1306,17 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1278 1278
 	return nil
1279 1279
 }
1280 1280
 
1281
-func (cli *DockerCli) checkIfLogged(condition bool, action string) (string, error) {
1282
-	body, _, err := cli.call("GET", "/auth", nil)
1283
-	if err != nil {
1284
-		return "", err
1285
-	}
1286
-
1287
-	var out auth.AuthConfig
1288
-	err = json.Unmarshal(body, &out)
1289
-	if err != nil {
1290
-		return "", err
1291
-	}
1292
-
1281
+func (cli *DockerCli) checkIfLogged(condition bool, action string) error {
1293 1282
 	// If condition AND the login failed
1294
-	if condition && out.Username == "" {
1283
+	if condition && cli.authConfig.Username == "" {
1295 1284
 		if err := cli.CmdLogin(""); err != nil {
1296
-			return "", err
1297
-		}
1298
-
1299
-		body, _, err = cli.call("GET", "/auth", nil)
1300
-		if err != nil {
1301
-			return "", err
1302
-		}
1303
-		err = json.Unmarshal(body, &out)
1304
-		if err != nil {
1305
-			return "", err
1285
+			return err
1306 1286
 		}
1307
-
1308
-		if out.Username == "" {
1309
-			return "", fmt.Errorf("Please login prior to %s. ('docker login')", action)
1287
+		if cli.authConfig.Username == "" {
1288
+			return fmt.Errorf("Please login prior to %s. ('docker login')", action)
1310 1289
 		}
1311 1290
 	}
1312
-	return out.Username, nil
1291
+	return nil
1313 1292
 }
1314 1293
 
1315 1294
 func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, error) {
... ...
@@ -1345,7 +1352,10 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
1345 1345
 		return nil, -1, err
1346 1346
 	}
1347 1347
 	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
1348
-		return nil, resp.StatusCode, fmt.Errorf("error: %s", body)
1348
+		if len(body) == 0 {
1349
+			return nil, resp.StatusCode, fmt.Errorf("Error: %s", http.StatusText(resp.StatusCode))
1350
+		}
1351
+		return nil, resp.StatusCode, fmt.Errorf("Error: %s", body)
1349 1352
 	}
1350 1353
 	return body, resp.StatusCode, nil
1351 1354
 }
... ...
@@ -1375,7 +1385,10 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
1375 1375
 		if err != nil {
1376 1376
 			return err
1377 1377
 		}
1378
-		return fmt.Errorf("error: %s", body)
1378
+		if len(body) == 0 {
1379
+			return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode))
1380
+		}
1381
+		return fmt.Errorf("Error: %s", body)
1379 1382
 	}
1380 1383
 
1381 1384
 	if resp.Header.Get("Content-Type") == "application/json" {
... ...
@@ -1433,19 +1446,22 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
1433 1433
 		defer term.RestoreTerminal(oldState)
1434 1434
 	}
1435 1435
 	sendStdin := utils.Go(func() error {
1436
-		_, err := io.Copy(rwc, in)
1436
+		io.Copy(rwc, in)
1437 1437
 		if err := rwc.(*net.TCPConn).CloseWrite(); err != nil {
1438
-			fmt.Fprintf(os.Stderr, "Couldn't send EOF: %s\n", err)
1438
+			utils.Debugf("Couldn't send EOF: %s\n", err)
1439 1439
 		}
1440
-		return err
1440
+		// Discard errors due to pipe interruption
1441
+		return nil
1441 1442
 	})
1442 1443
 
1443 1444
 	if err := <-receiveStdout; err != nil {
1445
+		utils.Debugf("Error receiveStdout: %s", err)
1444 1446
 		return err
1445 1447
 	}
1446 1448
 
1447
-	if !term.IsTerminal(os.Stdin.Fd()) {
1449
+	if !term.IsTerminal(in.Fd()) {
1448 1450
 		if err := <-sendStdin; err != nil {
1451
+			utils.Debugf("Error sendStdin: %s", err)
1449 1452
 			return err
1450 1453
 		}
1451 1454
 	}
... ...
@@ -1490,10 +1506,12 @@ func Subcmd(name, signature, description string) *flag.FlagSet {
1490 1490
 }
1491 1491
 
1492 1492
 func NewDockerCli(addr string, port int) *DockerCli {
1493
-	return &DockerCli{addr, port}
1493
+	authConfig, _ := auth.LoadConfig(os.Getenv("HOME"))
1494
+	return &DockerCli{addr, port, authConfig}
1494 1495
 }
1495 1496
 
1496 1497
 type DockerCli struct {
1497
-	host string
1498
-	port int
1498
+	host       string
1499
+	port       int
1500
+	authConfig *auth.AuthConfig
1499 1501
 }
... ...
@@ -356,6 +356,18 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
356 356
 				errors <- err
357 357
 			}()
358 358
 		}
359
+	} else {
360
+		go func() {
361
+			if stdinCloser != nil {
362
+				defer stdinCloser.Close()
363
+			}
364
+
365
+			if cStdout, err := container.StdoutPipe(); err != nil {
366
+				utils.Debugf("Error stdout pipe")
367
+			} else {
368
+				io.Copy(&utils.NopWriter{}, cStdout)
369
+			}
370
+		}()
359 371
 	}
360 372
 	if stderr != nil {
361 373
 		nJobs += 1
... ...
@@ -382,7 +394,20 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
382 382
 				errors <- err
383 383
 			}()
384 384
 		}
385
+	} else {
386
+		go func() {
387
+			if stdinCloser != nil {
388
+				defer stdinCloser.Close()
389
+			}
390
+
391
+			if cStderr, err := container.StderrPipe(); err != nil {
392
+				utils.Debugf("Error stdout pipe")
393
+			} else {
394
+				io.Copy(&utils.NopWriter{}, cStderr)
395
+			}
396
+		}()
385 397
 	}
398
+
386 399
 	return utils.Go(func() error {
387 400
 		if cStdout != nil {
388 401
 			defer cStdout.Close()
389 402
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!
... ...
@@ -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
... ...
@@ -65,7 +66,7 @@ func main() {
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/ ; \
65
+	@echo or create your own "dockerwebsite" app
66
+	@cd $(BUILDDIR)/website/ ; \
66 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
... ...
@@ -14,719 +14,44 @@ Docker Remote API
14 14
 - The Remote API is replacing rcli
15 15
 - Default port in the docker deamon is 4243 
16 16
 - The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
17
+- Since API version 1.2, the auth configuration is now handled client side, so the client has to send the authConfig as POST in /images/(name)/push
17 18
 
18
-2. Version
19
-==========
19
+2. Versions
20
+===========
20 21
 
21
-The current verson of the API is 1.1
22
-Calling /images/<name>/insert is the same as calling /v1.1/images/<name>/insert
22
+The current verson of the API is 1.2
23
+Calling /images/<name>/insert is the same as calling /v1.2/images/<name>/insert
23 24
 You can still call an old version of the api using /v1.0/images/<name>/insert
24 25
 
25
-3. Endpoints
26
-============
27
-
28
-3.1 Containers
29
-
30
-List containers
31
-***************
32
-
33
-.. http:get:: /containers/json
34
-
35
-	List containers
36
-
37
-	**Example request**:
38
-
39
-	.. sourcecode:: http
40
-
41
-	   GET /containers/json?all=1&before=8dfafdbc3a40 HTTP/1.1
42
-	   
43
-	**Example response**:
44
-
45
-	.. sourcecode:: http
46
-
47
-	   HTTP/1.1 200 OK
48
-	   Content-Type: application/json
49
-	   
50
-	   [
51
-		{
52
-			"Id": "8dfafdbc3a40",
53
-			"Image": "base:latest",
54
-			"Command": "echo 1",
55
-			"Created": 1367854155,
56
-			"Status": "Exit 0"
57
-		},
58
-		{
59
-			"Id": "9cd87474be90",
60
-			"Image": "base:latest",
61
-			"Command": "echo 222222",
62
-			"Created": 1367854155,
63
-			"Status": "Exit 0"
64
-		},
65
-		{
66
-			"Id": "3176a2479c92",
67
-			"Image": "base:latest",
68
-			"Command": "echo 3333333333333333",
69
-			"Created": 1367854154,
70
-			"Status": "Exit 0"
71
-		},
72
-		{
73
-			"Id": "4cb07b47f9fb",
74
-			"Image": "base:latest",
75
-			"Command": "echo 444444444444444444444444444444444",
76
-			"Created": 1367854152,
77
-			"Status": "Exit 0"
78
-		}
79
-	   ]
80
- 
81
-	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
82
-	:query limit: Show ``limit`` last created containers, include non-running ones.
83
-	:query since: Show only containers created since Id, include non-running ones.
84
-	:query before: Show only containers created before Id, include non-running ones.
85
-	:statuscode 200: no error
86
-	:statuscode 400: bad parameter
87
-	:statuscode 500: server error
88
-
89
-
90
-Create a container
91
-******************
92
-
93
-.. http:post:: /containers/create
94
-
95
-	Create a container
96
-
97
-	**Example request**:
98
-
99
-	.. sourcecode:: http
100
-
101
-	   POST /containers/create HTTP/1.1
102
-	   Content-Type: application/json
103
-
104
-	   {
105
-		"Hostname":"",
106
-		"User":"",
107
-		"Memory":0,
108
-		"MemorySwap":0,
109
-		"AttachStdin":false,
110
-		"AttachStdout":true,
111
-		"AttachStderr":true,
112
-		"PortSpecs":null,
113
-		"Tty":false,
114
-		"OpenStdin":false,
115
-		"StdinOnce":false,
116
-		"Env":null,
117
-		"Cmd":[
118
-			"date"
119
-		],
120
-		"Dns":null,
121
-		"Image":"base",
122
-		"Volumes":{},
123
-		"VolumesFrom":""
124
-	   }
125
-	   
126
-	**Example response**:
127
-
128
-	.. sourcecode:: http
129
-
130
-	   HTTP/1.1 201 OK
131
-	   Content-Type: application/json
132
-
133
-	   {
134
-		"Id":"e90e34656806"
135
-		"Warnings":[]
136
-	   }
137
-	
138
-	:jsonparam config: the container's configuration
139
-	:statuscode 201: no error
140
-	:statuscode 404: no such container
141
-	:statuscode 406: impossible to attach (container not running)
142
-	:statuscode 500: server error
143
-
144
-
145
-Inspect a container
146
-*******************
147
-
148
-.. http:get:: /containers/(id)/json
149
-
150
-	Return low-level information on the container ``id``
151
-
152
-	**Example request**:
153
-
154
-	.. sourcecode:: http
155
-
156
-	   GET /containers/4fa6e0f0c678/json HTTP/1.1
157
-	   
158
-	**Example response**:
159
-
160
-	.. sourcecode:: http
161
-
162
-	   HTTP/1.1 200 OK
163
-	   Content-Type: application/json
164
-
165
-	   {
166
-			"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
167
-			"Created": "2013-05-07T14:51:42.041847+02:00",
168
-			"Path": "date",
169
-			"Args": [],
170
-			"Config": {
171
-				"Hostname": "4fa6e0f0c678",
172
-				"User": "",
173
-				"Memory": 0,
174
-				"MemorySwap": 0,
175
-				"AttachStdin": false,
176
-				"AttachStdout": true,
177
-				"AttachStderr": true,
178
-				"PortSpecs": null,
179
-				"Tty": false,
180
-				"OpenStdin": false,
181
-				"StdinOnce": false,
182
-				"Env": null,
183
-				"Cmd": [
184
-					"date"
185
-				],
186
-				"Dns": null,
187
-				"Image": "base",
188
-				"Volumes": {},
189
-				"VolumesFrom": ""
190
-			},
191
-			"State": {
192
-				"Running": false,
193
-				"Pid": 0,
194
-				"ExitCode": 0,
195
-				"StartedAt": "2013-05-07T14:51:42.087658+02:01360",
196
-				"Ghost": false
197
-			},
198
-			"Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
199
-			"NetworkSettings": {
200
-				"IpAddress": "",
201
-				"IpPrefixLen": 0,
202
-				"Gateway": "",
203
-				"Bridge": "",
204
-				"PortMapping": null
205
-			},
206
-			"SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
207
-			"ResolvConfPath": "/etc/resolv.conf",
208
-			"Volumes": {}
209
-	   }
210
-
211
-	:statuscode 200: no error
212
-	:statuscode 404: no such container
213
-	:statuscode 500: server error
214
-
215
-
216
-Inspect changes on a container's filesystem
217
-*******************************************
218
-
219
-.. http:get:: /containers/(id)/changes
220
-
221
-	Inspect changes on container ``id`` 's filesystem
222
-
223
-	**Example request**:
224
-
225
-	.. sourcecode:: http
226
-
227
-	   GET /containers/4fa6e0f0c678/changes HTTP/1.1
228
-
229
-	   
230
-	**Example response**:
231
-
232
-	.. sourcecode:: http
233
-
234
-	   HTTP/1.1 200 OK
235
-	   Content-Type: application/json
236
-	   
237
-	   [
238
-		{
239
-			"Path":"/dev",
240
-			"Kind":0
241
-		},
242
-		{
243
-			"Path":"/dev/kmsg",
244
-			"Kind":1
245
-		},
246
-		{
247
-			"Path":"/test",
248
-			"Kind":1
249
-		}
250
-	   ]
251
-
252
-	:statuscode 200: no error
253
-	:statuscode 404: no such container
254
-	:statuscode 500: server error
255
-
256
-
257
-Export a container
258
-******************
259
-
260
-.. http:get:: /containers/(id)/export
261
-
262
-	Export the contents of container ``id``
263
-
264
-	**Example request**:
265
-
266
-	.. sourcecode:: http
267
-
268
-	   GET /containers/4fa6e0f0c678/export HTTP/1.1
269
-
270
-	   
271
-	**Example response**:
272
-
273
-	.. sourcecode:: http
274
-
275
-	   HTTP/1.1 200 OK
276
-	   Content-Type: application/octet-stream
277
-	   
278
-	   {{ STREAM }}
279
-
280
-	:statuscode 200: no error
281
-	:statuscode 404: no such container
282
-	:statuscode 500: server error
283
-
284
-
285
-Start a container
286
-*****************
287
-
288
-.. http:post:: /containers/(id)/start
289
-
290
-	Start the container ``id``
291
-
292
-	**Example request**:
293
-
294
-	.. sourcecode:: http
295
-
296
-	   POST /containers/e90e34656806/start HTTP/1.1
297
-	   
298
-	**Example response**:
299
-
300
-	.. sourcecode:: http
301
-
302
-	   HTTP/1.1 200 OK
303
-	   	
304
-	:statuscode 200: no error
305
-	:statuscode 404: no such container
306
-	:statuscode 500: server error
307
-
308
-
309
-Stop a contaier
310
-***************
311
-
312
-.. http:post:: /containers/(id)/stop
313
-
314
-	Stop the container ``id``
315
-
316
-	**Example request**:
317
-
318
-	.. sourcecode:: http
319
-
320
-	   POST /containers/e90e34656806/stop?t=5 HTTP/1.1
321
-	   
322
-	**Example response**:
323
-
324
-	.. sourcecode:: http
325
-
326
-	   HTTP/1.1 204 OK
327
-	   	
328
-	:query t: number of seconds to wait before killing the container
329
-	:statuscode 204: no error
330
-	:statuscode 404: no such container
331
-	:statuscode 500: server error
332
-
333
-
334
-Restart a container
335
-*******************
336
-
337
-.. http:post:: /containers/(id)/restart
338
-
339
-	Restart the container ``id``
340
-
341
-	**Example request**:
342
-
343
-	.. sourcecode:: http
344
-
345
-	   POST /containers/e90e34656806/restart?t=5 HTTP/1.1
346
-	   
347
-	**Example response**:
348
-
349
-	.. sourcecode:: http
350
-
351
-	   HTTP/1.1 204 OK
352
-	   	
353
-	:query t: number of seconds to wait before killing the container
354
-	:statuscode 204: no error
355
-	:statuscode 404: no such container
356
-	:statuscode 500: server error
357
-
358
-
359
-Kill a container
360
-****************
361
-
362
-.. http:post:: /containers/(id)/kill
363
-
364
-	Kill the container ``id``
365
-
366
-	**Example request**:
367
-
368
-	.. sourcecode:: http
369
-
370
-	   POST /containers/e90e34656806/kill HTTP/1.1
371
-	   
372
-	**Example response**:
373
-
374
-	.. sourcecode:: http
375
-
376
-	   HTTP/1.1 204 OK
377
-	   	
378
-	:statuscode 204: no error
379
-	:statuscode 404: no such container
380
-	:statuscode 500: server error
381
-
382
-
383
-Attach to a container
384
-*********************
385
-
386
-.. http:post:: /containers/(id)/attach
387
-
388
-	Attach to the container ``id``
389
-
390
-	**Example request**:
391
-
392
-	.. sourcecode:: http
393
-
394
-	   POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1
395
-	   
396
-	**Example response**:
397
-
398
-	.. sourcecode:: http
399
-
400
-	   HTTP/1.1 200 OK
401
-	   Content-Type: application/vnd.docker.raw-stream
402
-
403
-	   {{ STREAM }}
404
-	   	
405
-	:query logs: 1/True/true or 0/False/false, return logs. Default false
406
-	:query stream: 1/True/true or 0/False/false, return stream. Default false
407
-	:query stdin: 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false
408
-	:query stdout: 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false
409
-	:query stderr: 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false
410
-	:statuscode 200: no error
411
-	:statuscode 400: bad parameter
412
-	:statuscode 404: no such container
413
-	:statuscode 500: server error
414
-
415
-
416
-Wait a container
417
-****************
418
-
419
-.. http:post:: /containers/(id)/wait
420
-
421
-	Block until container ``id`` stops, then returns the exit code
422
-
423
-	**Example request**:
424
-
425
-	.. sourcecode:: http
426
-
427
-	   POST /containers/16253994b7c4/wait HTTP/1.1
428
-	   
429
-	**Example response**:
430
-
431
-	.. sourcecode:: http
432
-
433
-	   HTTP/1.1 200 OK
434
-	   Content-Type: application/json
435
-
436
-	   {"StatusCode":0}
437
-	   	
438
-	:statuscode 200: no error
439
-	:statuscode 404: no such container
440
-	:statuscode 500: server error
441
-
442
-
443
-Remove a container
444
-*******************
445
-
446
-.. http:delete:: /containers/(id)
447
-
448
-	Remove the container ``id`` from the filesystem
449
-
450
-	**Example request**:
451
-
452
-        .. sourcecode:: http
453
-
454
-           DELETE /containers/16253994b7c4?v=1 HTTP/1.1
455
-
456
-        **Example response**:
457
-
458
-        .. sourcecode:: http
459
-
460
-	   HTTP/1.1 204 OK
461
-
462
-	:query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false
463
-        :statuscode 204: no error
464
-	:statuscode 400: bad parameter
465
-        :statuscode 404: no such container
466
-        :statuscode 500: server error
467
-
26
+:doc:`docker_remote_api_v1.2`
27
+*****************************
468 28
 
469
-3.2 Images
29
+What's new
470 30
 ----------
471 31
 
472
-List Images
473
-***********
474
-
475
-.. http:get:: /images/(format)
476
-
477
-	List images ``format`` could be json or viz (json default)
478
-
479
-	**Example request**:
480
-
481
-	.. sourcecode:: http
482
-
483
-	   GET /images/json?all=0 HTTP/1.1
484
-
485
-	**Example response**:
486
-
487
-	.. sourcecode:: http
488
-
489
-	   HTTP/1.1 200 OK
490
-	   Content-Type: application/json
491
-	   
492
-	   [
493
-		{
494
-			"Repository":"base",
495
-			"Tag":"ubuntu-12.10",
496
-			"Id":"b750fe79269d",
497
-			"Created":1364102658
498
-		},
499
-		{
500
-			"Repository":"base",
501
-			"Tag":"ubuntu-quantal",
502
-			"Id":"b750fe79269d",
503
-			"Created":1364102658
504
-		}
505
-	   ]
506
-
507
-
508
-	**Example request**:
509
-
510
-	.. sourcecode:: http
32
+The auth configuration is now handled by the client.
33
+The client should send it's authConfig as POST on each call of /images/(name)/push
511 34
 
512
-	   GET /images/viz HTTP/1.1
35
+.. http:get:: /auth is now deprecated
36
+.. http:post:: /auth only checks the configuration but doesn't store it on the server
513 37
 
514
-	**Example response**:
38
+Deleting an image is now improved, will only untag the image if it has chidrens and remove all the untagged parents if has any.
39
+.. http:post:: /images/<name>/delete now returns a JSON with the list of images deleted/untagged
515 40
 
516
-	.. sourcecode:: http
517 41
 
518
-	   HTTP/1.1 200 OK
519
-	   Content-Type: text/plain
520
-
521
-	   digraph docker {
522
-	   "d82cbacda43a" -> "074be284591f"
523
-	   "1496068ca813" -> "08306dc45919"
524
-	   "08306dc45919" -> "0e7893146ac2"
525
-	   "b750fe79269d" -> "1496068ca813"
526
-	   base -> "27cf78414709" [style=invis]
527
-	   "f71189fff3de" -> "9a33b36209ed"
528
-	   "27cf78414709" -> "b750fe79269d"
529
-	   "0e7893146ac2" -> "d6434d954665"
530
-	   "d6434d954665" -> "d82cbacda43a"
531
-	   base -> "e9aa60c60128" [style=invis]
532
-	   "074be284591f" -> "f71189fff3de"
533
-	   "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
534
-	   "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
535
-	   "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
536
-	   base [style=invisible]
537
-	   }
538
- 
539
-	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
540
-	:statuscode 200: no error
541
-	:statuscode 400: bad parameter
542
-	:statuscode 500: server error
42
+:doc:`docker_remote_api_v1.1`
43
+*****************************
543 44
 
45
+docker v0.4.0 a8ae398_
544 46
 
545
-Create an image
546
-***************
47
+What's new
48
+----------
547 49
 
548 50
 .. http:post:: /images/create
549
-
550
-	Create an image, either by pull it from the registry or by importing it
551
-
552
-	**Example request**:
553
-
554
-        .. sourcecode:: http
555
-
556
-           POST /images/create?fromImage=base HTTP/1.1
557
-
558
-        **Example response v1.1**:
559
-
560
-        .. sourcecode:: http
561
-
562
-           HTTP/1.1 200 OK
563
-	   Content-Type: application/json
564
-
565
-	   {"status":"Pulling..."}
566
-	   {"status":"Pulling", "progress":"1/? (n/a)"}
567
-	   {"error":"Invalid..."}
568
-	   ...
569
-
570
-        **Example response v1.0**:
571
-
572
-        .. sourcecode:: http
573
-
574
-           HTTP/1.1 200 OK
575
-	   Content-Type: application/vnd.docker.raw-stream
576
-
577
-	   {{ STREAM }}
578
-
579
-        :query fromImage: name of the image to pull
580
-	:query fromSrc: source to import, - means stdin
581
-        :query repo: repository
582
-	:query tag: tag
583
-	:query registry: the registry to pull from
584
-        :statuscode 200: no error
585
-        :statuscode 500: server error
586
-
587
-
588
-Insert a file in a image
589
-************************
590
-
591 51
 .. http:post:: /images/(name)/insert
592
-
593
-	Insert a file from ``url`` in the image ``name`` at ``path``
594
-
595
-	**Example request**:
596
-
597
-        .. sourcecode:: http
598
-
599
-           POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
600
-
601
-	**Example response v1.1**:
602
-
603
-        .. sourcecode:: http
604
-
605
-           HTTP/1.1 200 OK
606
-	   Content-Type: application/json
607
-
608
-	   {"status":"Inserting..."}
609
-	   {"status":"Inserting", "progress":"1/? (n/a)"}
610
-	   {"error":"Invalid..."}
611
-	   ...
612
-
613
-	**Example response v1.0**:
614
-
615
-        .. sourcecode:: http
616
-
617
-           HTTP/1.1 200 OK
618
-
619
-	   {{ STREAM }}
620
-
621
-	:statuscode 200: no error
622
-        :statuscode 500: server error
623
-
624
-
625
-Inspect an image
626
-****************
627
-
628
-.. http:get:: /images/(name)/json
629
-
630
-	Return low-level information on the image ``name``
631
-
632
-	**Example request**:
633
-
634
-	.. sourcecode:: http
635
-
636
-	   GET /images/base/json HTTP/1.1
637
-
638
-	**Example response**:
639
-
640
-        .. sourcecode:: http
641
-
642
-           HTTP/1.1 200 OK
643
-	   Content-Type: application/json
644
-
645
-	   {
646
-		"id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
647
-		"parent":"27cf784147099545",
648
-		"created":"2013-03-23T22:24:18.818426-07:00",
649
-		"container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
650
-		"container_config":
651
-			{
652
-				"Hostname":"",
653
-				"User":"",
654
-				"Memory":0,
655
-				"MemorySwap":0,
656
-				"AttachStdin":false,
657
-				"AttachStdout":false,
658
-				"AttachStderr":false,
659
-				"PortSpecs":null,
660
-				"Tty":true,
661
-				"OpenStdin":true,
662
-				"StdinOnce":false,
663
-				"Env":null,
664
-				"Cmd": ["/bin/bash"]
665
-				,"Dns":null,
666
-				"Image":"base",
667
-				"Volumes":null,
668
-				"VolumesFrom":""
669
-			}
670
-	   }
671
-
672
-	:statuscode 200: no error
673
-	:statuscode 404: no such image
674
-        :statuscode 500: server error
675
-
676
-
677
-Get the history of an image
678
-***************************
679
-
680
-.. http:get:: /images/(name)/history
681
-
682
-        Return the history of the image ``name``
683
-
684
-        **Example request**:
685
-
686
-        .. sourcecode:: http
687
-
688
-           GET /images/base/history HTTP/1.1
689
-
690
-        **Example response**:
691
-
692
-        .. sourcecode:: http
693
-
694
-           HTTP/1.1 200 OK
695
-	   Content-Type: application/json
696
-
697
-	   [
698
-		{
699
-			"Id":"b750fe79269d",
700
-			"Created":1364102658,
701
-			"CreatedBy":"/bin/bash"
702
-		},
703
-		{
704
-			"Id":"27cf78414709",
705
-			"Created":1364068391,
706
-			"CreatedBy":""
707
-		}
708
-	   ]
709
-
710
-        :statuscode 200: no error
711
-        :statuscode 404: no such image
712
-        :statuscode 500: server error
713
-
714
-
715
-Push an image on the registry
716
-*****************************
717
-
718 52
 .. http:post:: /images/(name)/push
719 53
 
720
-	Push the image ``name`` on the registry
721
-
722
-	 **Example request**:
723
-
724
-	 .. sourcecode:: http
725
-
726
-	    POST /images/test/push HTTP/1.1
727
-
728
-	 **Example response v1.1**:
54
+Uses json stream instead of HTML hijack, it looks like this:
729 55
 
730 56
         .. sourcecode:: http
731 57
 
... ...
@@ -738,321 +63,38 @@ Push an image on the registry
738 738
 	   {"error":"Invalid..."}
739 739
 	   ...
740 740
 
741
-	 **Example response v1.0**:
742
-
743
-        .. sourcecode:: http
744
-
745
-           HTTP/1.1 200 OK
746
-	   Content-Type: application/vnd.docker.raw-stream
747
-
748
-	   {{ STREAM }}
749
-
750
-	:query registry: the registry you wan to push, optional
751
-	:statuscode 200: no error
752
-        :statuscode 404: no such image
753
-        :statuscode 500: server error
754
-
755
-
756
-Tag an image into a repository
757
-******************************
758
-
759
-.. http:post:: /images/(name)/tag
760
-
761
-	Tag the image ``name`` into a repository
762
-
763
-        **Example request**:
764
-
765
-        .. sourcecode:: http
766
-			
767
-	   POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1
768
-
769
-	**Example response**:
770
-
771
-        .. sourcecode:: http
772
-
773
-           HTTP/1.1 200 OK
774
-
775
-	:query repo: The repository to tag in
776
-	:query force: 1/True/true or 0/False/false, default false
777
-	:statuscode 200: no error
778
-	:statuscode 400: bad parameter
779
-	:statuscode 404: no such image
780
-        :statuscode 500: server error
781
-
782
-
783
-Remove an image
784
-***************
785
-
786
-.. http:delete:: /images/(name)
787
-
788
-	Remove the image ``name`` from the filesystem 
789
-	
790
-	**Example request**:
791
-
792
-	.. sourcecode:: http
793
-
794
-	   DELETE /images/test HTTP/1.1
795
-
796
-	**Example response**:
797
-
798
-        .. sourcecode:: http
799
-
800
-           HTTP/1.1 204 OK
801
-
802
-	:statuscode 204: no error
803
-        :statuscode 404: no such image
804
-        :statuscode 500: server error
805
-
806
-
807
-Search images
808
-*************
809
-
810
-.. http:get:: /images/search
811
-
812
-	Search for an image in the docker index
813
-	
814
-	**Example request**:
815
-
816
-        .. sourcecode:: http
817
-
818
-           GET /images/search?term=sshd HTTP/1.1
819
-
820
-	**Example response**:
821
-
822
-	.. sourcecode:: http
823
-
824
-	   HTTP/1.1 200 OK
825
-	   Content-Type: application/json
826
-	   
827
-	   [
828
-		{
829
-			"Name":"cespare/sshd",
830
-			"Description":""
831
-		},
832
-		{
833
-			"Name":"johnfuller/sshd",
834
-			"Description":""
835
-		},
836
-		{
837
-			"Name":"dhrp/mongodb-sshd",
838
-			"Description":""
839
-		}
840
-	   ]
841
-
842
-	   :query term: term to search
843
-	   :statuscode 200: no error
844
-	   :statuscode 500: server error
845
-
846
-
847
-3.3 Misc
848
-
849
-Build an image from Dockerfile via stdin
850
-****************************************
851
-
852
-.. http:post:: /build
853
-
854
-	Build an image from Dockerfile via stdin
855
-
856
-	**Example request**:
857 741
 
858
-        .. sourcecode:: http
859
-
860
-           POST /build HTTP/1.1
861
-	   
862
-	   {{ STREAM }}
863
-
864
-	**Example response**:
865
-
866
-        .. sourcecode:: http
867
-
868
-           HTTP/1.1 200 OK
869
-	   
870
-	   {{ STREAM }}
871
-
872
-	:query t: tag to be applied to the resulting image in case of success
873
-	:statuscode 200: no error
874
-        :statuscode 500: server error
875
-
876
-
877
-Get default username and email
878
-******************************
879
-
880
-.. http:get:: /auth
881
-
882
-	Get the default username and email
883
-
884
-	**Example request**:
885
-
886
-        .. sourcecode:: http
887
-
888
-           GET /auth HTTP/1.1
889
-
890
-        **Example response**:
891
-
892
-        .. sourcecode:: http
893
-
894
-           HTTP/1.1 200 OK
895
-	   Content-Type: application/json
896
-
897
-	   {
898
-		"username":"hannibal",
899
-		"email":"hannibal@a-team.com"
900
-	   }
901
-
902
-        :statuscode 200: no error
903
-        :statuscode 500: server error
904
-
905
-
906
-Set auth configuration
907
-**********************
908
-
909
-.. http:post:: /auth
910
-
911
-        Get the default username and email
912
-
913
-        **Example request**:
914
-
915
-        .. sourcecode:: http
916
-
917
-           POST /auth HTTP/1.1
918
-	   Content-Type: application/json
919
-
920
-	   {
921
-		"username":"hannibal",
922
-		"password:"xxxx",
923
-		"email":"hannibal@a-team.com"
924
-	   }
925
-
926
-        **Example response**:
927
-
928
-        .. sourcecode:: http
929
-
930
-           HTTP/1.1 200 OK
931
-
932
-        :statuscode 200: no error
933
-        :statuscode 204: no error
934
-        :statuscode 500: server error
935
-
936
-
937
-Display system-wide information
938
-*******************************
939
-
940
-.. http:get:: /info
941
-
942
-	Display system-wide information
943
-	
944
-	**Example request**:
945
-
946
-        .. sourcecode:: http
742
+docker v0.3.4 8d73740_
947 743
 
948
-           GET /info HTTP/1.1
949
-
950
-        **Example response**:
951
-
952
-        .. sourcecode:: http
953
-
954
-           HTTP/1.1 200 OK
955
-	   Content-Type: application/json
956
-
957
-	   {
958
-		"Containers":11,
959
-		"Images":16,
960
-		"Debug":false,
961
-		"NFd": 11,
962
-		"NGoroutines":21,
963
-		"MemoryLimit":true,
964
-		"SwapLimit":false
965
-	   }
966
-
967
-        :statuscode 200: no error
968
-        :statuscode 500: server error
969
-
970
-
971
-Show the docker version information
972
-***********************************
973
-
974
-.. http:get:: /version
975
-
976
-	Show the docker version information
977
-
978
-	**Example request**:
979
-
980
-        .. sourcecode:: http
981
-
982
-           GET /version HTTP/1.1
983
-
984
-        **Example response**:
985
-
986
-        .. sourcecode:: http
987
-
988
-           HTTP/1.1 200 OK
989
-	   Content-Type: application/json
990
-
991
-	   {
992
-		"Version":"0.2.2",
993
-		"GitCommit":"5a2a5cc+CHANGES",
994
-		"GoVersion":"go1.0.3"
995
-	   }
996
-
997
-        :statuscode 200: no error
998
-	:statuscode 500: server error
999
-
1000
-
1001
-Create a new image from a container's changes
1002
-*********************************************
1003
-
1004
-.. http:post:: /commit
1005
-
1006
-	Create a new image from a container's changes
1007
-
1008
-	**Example request**:
1009
-
1010
-        .. sourcecode:: http
1011
-
1012
-           POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
1013
-
1014
-        **Example response**:
1015
-
1016
-        .. sourcecode:: http
1017
-
1018
-           HTTP/1.1 201 OK
1019
-	   Content-Type: application/vnd.docker.raw-stream
1020
-
1021
-           {"Id":"596069db4bf5"}
1022
-
1023
-	:query container: source container
1024
-	:query repo: repository
1025
-	:query tag: tag
1026
-	:query m: commit message
1027
-	:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
1028
-	:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
1029
-        :statuscode 201: no error
1030
-	:statuscode 404: no such container
1031
-        :statuscode 500: server error
1032
-
1033
-
1034
-3. Going further
1035
-================
1036
-
1037
-3.1 Inside 'docker run'
1038
-
1039
-Here are the steps of 'docker run' :
1040
-
1041
-* Create the container
1042
-* If the status code is 404, it means the image doesn't exists:
1043
-        * Try to pull it
1044
-        * Then retry to create the container
1045
-* Start the container
1046
-* If you are not in detached mode:
1047
-        * Attach to the container, using logs=1 (to have stdout and stderr from the container's start) and stream=1
1048
-* If in detached mode or only stdin is attached:
1049
-	* Display the container's id
1050
-
1051
-
1052
-3.2 Hijacking
744
+What's new
745
+----------
1053 746
 
1054
-In this first version of the API, some of the endpoints, like /attach, /pull or /push uses hijacking to transport stdin,
1055
-stdout and stderr on the same socket. This might change in the future.
747
+Initial version
748
+
749
+
750
+.. _a8ae398: https://github.com/dotcloud/docker/commit/a8ae398bf52e97148ee7bd0d5868de2e15bd297f
751
+.. _8d73740: https://github.com/dotcloud/docker/commit/8d73740343778651c09160cde9661f5f387b36f4
752
+
753
+==================================
754
+Docker Remote API Client Libraries
755
+==================================
756
+
757
+These libraries have been not tested by the Docker Maintainers for
758
+compatibility. Please file issues with the library owners.  If you
759
+find more library implementations, please list them in Docker doc bugs
760
+and we will add the libraries here.
761
+
762
++----------------------+----------------+--------------------------------------------+
763
+| Language/Framework   | Name           | Repository                                 |
764
++======================+================+============================================+
765
+| Python               | docker-py      | https://github.com/dotcloud/docker-py      |
766
++----------------------+----------------+--------------------------------------------+
767
+| Ruby                 | docker-ruby    | https://github.com/ActiveState/docker-ruby |
768
++----------------------+----------------+--------------------------------------------+
769
+| Ruby                 | docker-client  | https://github.com/geku/docker-client      |
770
++----------------------+----------------+--------------------------------------------+
771
+| Javascript           | docker-js      | https://github.com/dgoujard/docker-js      |
772
++----------------------+----------------+--------------------------------------------+
773
+| Javascript (Angular) | dockerui       | https://github.com/crosbymichael/dockerui  |
774
+| **WebUI**            |                |                                            |
775
++----------------------+----------------+--------------------------------------------+
1056 776
new file mode 100644
... ...
@@ -0,0 +1,1015 @@
0
+:title: Remote API v1.0
1
+:description: API Documentation for Docker
2
+:keywords: API, Docker, rcli, REST, documentation
3
+
4
+======================
5
+Docker Remote API v1.0
6
+======================
7
+
8
+.. contents:: Table of Contents
9
+
10
+1. Brief introduction
11
+=====================
12
+
13
+- The Remote API is replacing rcli
14
+- Default port in the docker deamon is 4243 
15
+- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
16
+
17
+2. Endpoints
18
+============
19
+
20
+2.1 Containers
21
+--------------
22
+
23
+List containers
24
+***************
25
+
26
+.. http:get:: /containers/json
27
+
28
+	List containers
29
+
30
+	**Example request**:
31
+
32
+	.. sourcecode:: http
33
+
34
+	   GET /containers/json?all=1&before=8dfafdbc3a40 HTTP/1.1
35
+	   
36
+	**Example response**:
37
+
38
+	.. sourcecode:: http
39
+
40
+	   HTTP/1.1 200 OK
41
+	   Content-Type: application/json
42
+	   
43
+	   [
44
+		{
45
+			"Id": "8dfafdbc3a40",
46
+			"Image": "base:latest",
47
+			"Command": "echo 1",
48
+			"Created": 1367854155,
49
+			"Status": "Exit 0"
50
+		},
51
+		{
52
+			"Id": "9cd87474be90",
53
+			"Image": "base:latest",
54
+			"Command": "echo 222222",
55
+			"Created": 1367854155,
56
+			"Status": "Exit 0"
57
+		},
58
+		{
59
+			"Id": "3176a2479c92",
60
+			"Image": "base:latest",
61
+			"Command": "echo 3333333333333333",
62
+			"Created": 1367854154,
63
+			"Status": "Exit 0"
64
+		},
65
+		{
66
+			"Id": "4cb07b47f9fb",
67
+			"Image": "base:latest",
68
+			"Command": "echo 444444444444444444444444444444444",
69
+			"Created": 1367854152,
70
+			"Status": "Exit 0"
71
+		}
72
+	   ]
73
+ 
74
+	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
75
+	:query limit: Show ``limit`` last created containers, include non-running ones.
76
+	:query since: Show only containers created since Id, include non-running ones.
77
+	:query before: Show only containers created before Id, include non-running ones.
78
+	:statuscode 200: no error
79
+	:statuscode 400: bad parameter
80
+	:statuscode 500: server error
81
+
82
+
83
+Create a container
84
+******************
85
+
86
+.. http:post:: /containers/create
87
+
88
+	Create a container
89
+
90
+	**Example request**:
91
+
92
+	.. sourcecode:: http
93
+
94
+	   POST /containers/create HTTP/1.1
95
+	   Content-Type: application/json
96
+
97
+	   {
98
+		"Hostname":"",
99
+		"User":"",
100
+		"Memory":0,
101
+		"MemorySwap":0,
102
+		"AttachStdin":false,
103
+		"AttachStdout":true,
104
+		"AttachStderr":true,
105
+		"PortSpecs":null,
106
+		"Tty":false,
107
+		"OpenStdin":false,
108
+		"StdinOnce":false,
109
+		"Env":null,
110
+		"Cmd":[
111
+			"date"
112
+		],
113
+		"Dns":null,
114
+		"Image":"base",
115
+		"Volumes":{},
116
+		"VolumesFrom":""
117
+	   }
118
+	   
119
+	**Example response**:
120
+
121
+	.. sourcecode:: http
122
+
123
+	   HTTP/1.1 201 OK
124
+	   Content-Type: application/json
125
+
126
+	   {
127
+		"Id":"e90e34656806"
128
+		"Warnings":[]
129
+	   }
130
+	
131
+	:jsonparam config: the container's configuration
132
+	:statuscode 201: no error
133
+	:statuscode 404: no such container
134
+	:statuscode 406: impossible to attach (container not running)
135
+	:statuscode 500: server error
136
+
137
+
138
+Inspect a container
139
+*******************
140
+
141
+.. http:get:: /containers/(id)/json
142
+
143
+	Return low-level information on the container ``id``
144
+
145
+	**Example request**:
146
+
147
+	.. sourcecode:: http
148
+
149
+	   GET /containers/4fa6e0f0c678/json HTTP/1.1
150
+	   
151
+	**Example response**:
152
+
153
+	.. sourcecode:: http
154
+
155
+	   HTTP/1.1 200 OK
156
+	   Content-Type: application/json
157
+
158
+	   {
159
+			"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
160
+			"Created": "2013-05-07T14:51:42.041847+02:00",
161
+			"Path": "date",
162
+			"Args": [],
163
+			"Config": {
164
+				"Hostname": "4fa6e0f0c678",
165
+				"User": "",
166
+				"Memory": 0,
167
+				"MemorySwap": 0,
168
+				"AttachStdin": false,
169
+				"AttachStdout": true,
170
+				"AttachStderr": true,
171
+				"PortSpecs": null,
172
+				"Tty": false,
173
+				"OpenStdin": false,
174
+				"StdinOnce": false,
175
+				"Env": null,
176
+				"Cmd": [
177
+					"date"
178
+				],
179
+				"Dns": null,
180
+				"Image": "base",
181
+				"Volumes": {},
182
+				"VolumesFrom": ""
183
+			},
184
+			"State": {
185
+				"Running": false,
186
+				"Pid": 0,
187
+				"ExitCode": 0,
188
+				"StartedAt": "2013-05-07T14:51:42.087658+02:01360",
189
+				"Ghost": false
190
+			},
191
+			"Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
192
+			"NetworkSettings": {
193
+				"IpAddress": "",
194
+				"IpPrefixLen": 0,
195
+				"Gateway": "",
196
+				"Bridge": "",
197
+				"PortMapping": null
198
+			},
199
+			"SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
200
+			"ResolvConfPath": "/etc/resolv.conf",
201
+			"Volumes": {}
202
+	   }
203
+
204
+	:statuscode 200: no error
205
+	:statuscode 404: no such container
206
+	:statuscode 500: server error
207
+
208
+
209
+Inspect changes on a container's filesystem
210
+*******************************************
211
+
212
+.. http:get:: /containers/(id)/changes
213
+
214
+	Inspect changes on container ``id`` 's filesystem
215
+
216
+	**Example request**:
217
+
218
+	.. sourcecode:: http
219
+
220
+	   GET /containers/4fa6e0f0c678/changes HTTP/1.1
221
+
222
+	   
223
+	**Example response**:
224
+
225
+	.. sourcecode:: http
226
+
227
+	   HTTP/1.1 200 OK
228
+	   Content-Type: application/json
229
+	   
230
+	   [
231
+		{
232
+			"Path":"/dev",
233
+			"Kind":0
234
+		},
235
+		{
236
+			"Path":"/dev/kmsg",
237
+			"Kind":1
238
+		},
239
+		{
240
+			"Path":"/test",
241
+			"Kind":1
242
+		}
243
+	   ]
244
+
245
+	:statuscode 200: no error
246
+	:statuscode 404: no such container
247
+	:statuscode 500: server error
248
+
249
+
250
+Export a container
251
+******************
252
+
253
+.. http:get:: /containers/(id)/export
254
+
255
+	Export the contents of container ``id``
256
+
257
+	**Example request**:
258
+
259
+	.. sourcecode:: http
260
+
261
+	   GET /containers/4fa6e0f0c678/export HTTP/1.1
262
+
263
+	   
264
+	**Example response**:
265
+
266
+	.. sourcecode:: http
267
+
268
+	   HTTP/1.1 200 OK
269
+	   Content-Type: application/octet-stream
270
+	   
271
+	   {{ STREAM }}
272
+
273
+	:statuscode 200: no error
274
+	:statuscode 404: no such container
275
+	:statuscode 500: server error
276
+
277
+
278
+Start a container
279
+*****************
280
+
281
+.. http:post:: /containers/(id)/start
282
+
283
+	Start the container ``id``
284
+
285
+	**Example request**:
286
+
287
+	.. sourcecode:: http
288
+
289
+	   POST /containers/e90e34656806/start HTTP/1.1
290
+	   
291
+	**Example response**:
292
+
293
+	.. sourcecode:: http
294
+
295
+	   HTTP/1.1 200 OK
296
+	   	
297
+	:statuscode 200: no error
298
+	:statuscode 404: no such container
299
+	:statuscode 500: server error
300
+
301
+
302
+Stop a contaier
303
+***************
304
+
305
+.. http:post:: /containers/(id)/stop
306
+
307
+	Stop the container ``id``
308
+
309
+	**Example request**:
310
+
311
+	.. sourcecode:: http
312
+
313
+	   POST /containers/e90e34656806/stop?t=5 HTTP/1.1
314
+	   
315
+	**Example response**:
316
+
317
+	.. sourcecode:: http
318
+
319
+	   HTTP/1.1 204 OK
320
+	   	
321
+	:query t: number of seconds to wait before killing the container
322
+	:statuscode 204: no error
323
+	:statuscode 404: no such container
324
+	:statuscode 500: server error
325
+
326
+
327
+Restart a container
328
+*******************
329
+
330
+.. http:post:: /containers/(id)/restart
331
+
332
+	Restart the container ``id``
333
+
334
+	**Example request**:
335
+
336
+	.. sourcecode:: http
337
+
338
+	   POST /containers/e90e34656806/restart?t=5 HTTP/1.1
339
+	   
340
+	**Example response**:
341
+
342
+	.. sourcecode:: http
343
+
344
+	   HTTP/1.1 204 OK
345
+	   	
346
+	:query t: number of seconds to wait before killing the container
347
+	:statuscode 204: no error
348
+	:statuscode 404: no such container
349
+	:statuscode 500: server error
350
+
351
+
352
+Kill a container
353
+****************
354
+
355
+.. http:post:: /containers/(id)/kill
356
+
357
+	Kill the container ``id``
358
+
359
+	**Example request**:
360
+
361
+	.. sourcecode:: http
362
+
363
+	   POST /containers/e90e34656806/kill HTTP/1.1
364
+	   
365
+	**Example response**:
366
+
367
+	.. sourcecode:: http
368
+
369
+	   HTTP/1.1 204 OK
370
+	   	
371
+	:statuscode 204: no error
372
+	:statuscode 404: no such container
373
+	:statuscode 500: server error
374
+
375
+
376
+Attach to a container
377
+*********************
378
+
379
+.. http:post:: /containers/(id)/attach
380
+
381
+	Attach to the container ``id``
382
+
383
+	**Example request**:
384
+
385
+	.. sourcecode:: http
386
+
387
+	   POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1
388
+	   
389
+	**Example response**:
390
+
391
+	.. sourcecode:: http
392
+
393
+	   HTTP/1.1 200 OK
394
+	   Content-Type: application/vnd.docker.raw-stream
395
+
396
+	   {{ STREAM }}
397
+	   	
398
+	:query logs: 1/True/true or 0/False/false, return logs. Default false
399
+	:query stream: 1/True/true or 0/False/false, return stream. Default false
400
+	:query stdin: 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false
401
+	:query stdout: 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false
402
+	:query stderr: 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false
403
+	:statuscode 200: no error
404
+	:statuscode 400: bad parameter
405
+	:statuscode 404: no such container
406
+	:statuscode 500: server error
407
+
408
+
409
+Wait a container
410
+****************
411
+
412
+.. http:post:: /containers/(id)/wait
413
+
414
+	Block until container ``id`` stops, then returns the exit code
415
+
416
+	**Example request**:
417
+
418
+	.. sourcecode:: http
419
+
420
+	   POST /containers/16253994b7c4/wait HTTP/1.1
421
+	   
422
+	**Example response**:
423
+
424
+	.. sourcecode:: http
425
+
426
+	   HTTP/1.1 200 OK
427
+	   Content-Type: application/json
428
+
429
+	   {"StatusCode":0}
430
+	   	
431
+	:statuscode 200: no error
432
+	:statuscode 404: no such container
433
+	:statuscode 500: server error
434
+
435
+
436
+Remove a container
437
+*******************
438
+
439
+.. http:delete:: /containers/(id)
440
+
441
+	Remove the container ``id`` from the filesystem
442
+
443
+	**Example request**:
444
+
445
+        .. sourcecode:: http
446
+
447
+           DELETE /containers/16253994b7c4?v=1 HTTP/1.1
448
+
449
+        **Example response**:
450
+
451
+        .. sourcecode:: http
452
+
453
+	   HTTP/1.1 204 OK
454
+
455
+	:query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false
456
+        :statuscode 204: no error
457
+	:statuscode 400: bad parameter
458
+        :statuscode 404: no such container
459
+        :statuscode 500: server error
460
+
461
+
462
+2.2 Images
463
+----------
464
+
465
+List Images
466
+***********
467
+
468
+.. http:get:: /images/(format)
469
+
470
+	List images ``format`` could be json or viz (json default)
471
+
472
+	**Example request**:
473
+
474
+	.. sourcecode:: http
475
+
476
+	   GET /images/json?all=0 HTTP/1.1
477
+
478
+	**Example response**:
479
+
480
+	.. sourcecode:: http
481
+
482
+	   HTTP/1.1 200 OK
483
+	   Content-Type: application/json
484
+	   
485
+	   [
486
+		{
487
+			"Repository":"base",
488
+			"Tag":"ubuntu-12.10",
489
+			"Id":"b750fe79269d",
490
+			"Created":1364102658
491
+		},
492
+		{
493
+			"Repository":"base",
494
+			"Tag":"ubuntu-quantal",
495
+			"Id":"b750fe79269d",
496
+			"Created":1364102658
497
+		}
498
+	   ]
499
+
500
+
501
+	**Example request**:
502
+
503
+	.. sourcecode:: http
504
+
505
+	   GET /images/viz HTTP/1.1
506
+
507
+	**Example response**:
508
+
509
+	.. sourcecode:: http
510
+
511
+	   HTTP/1.1 200 OK
512
+	   Content-Type: text/plain
513
+
514
+	   digraph docker {
515
+	   "d82cbacda43a" -> "074be284591f"
516
+	   "1496068ca813" -> "08306dc45919"
517
+	   "08306dc45919" -> "0e7893146ac2"
518
+	   "b750fe79269d" -> "1496068ca813"
519
+	   base -> "27cf78414709" [style=invis]
520
+	   "f71189fff3de" -> "9a33b36209ed"
521
+	   "27cf78414709" -> "b750fe79269d"
522
+	   "0e7893146ac2" -> "d6434d954665"
523
+	   "d6434d954665" -> "d82cbacda43a"
524
+	   base -> "e9aa60c60128" [style=invis]
525
+	   "074be284591f" -> "f71189fff3de"
526
+	   "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
527
+	   "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
528
+	   "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
529
+	   base [style=invisible]
530
+	   }
531
+ 
532
+	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
533
+	:statuscode 200: no error
534
+	:statuscode 400: bad parameter
535
+	:statuscode 500: server error
536
+
537
+
538
+Create an image
539
+***************
540
+
541
+.. http:post:: /images/create
542
+
543
+	Create an image, either by pull it from the registry or by importing it
544
+
545
+	**Example request**:
546
+
547
+        .. sourcecode:: http
548
+
549
+           POST /images/create?fromImage=base HTTP/1.1
550
+
551
+        **Example response**:
552
+
553
+        .. sourcecode:: http
554
+
555
+           HTTP/1.1 200 OK
556
+	   Content-Type: application/vnd.docker.raw-stream
557
+
558
+	   {{ STREAM }}
559
+
560
+        :query fromImage: name of the image to pull
561
+	:query fromSrc: source to import, - means stdin
562
+        :query repo: repository
563
+	:query tag: tag
564
+	:query registry: the registry to pull from
565
+        :statuscode 200: no error
566
+        :statuscode 500: server error
567
+
568
+
569
+Insert a file in a image
570
+************************
571
+
572
+.. http:post:: /images/(name)/insert
573
+
574
+	Insert a file from ``url`` in the image ``name`` at ``path``
575
+
576
+	**Example request**:
577
+
578
+        .. sourcecode:: http
579
+
580
+           POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
581
+
582
+	**Example response**:
583
+
584
+        .. sourcecode:: http
585
+
586
+           HTTP/1.1 200 OK
587
+
588
+	   {{ STREAM }}
589
+
590
+	:statuscode 200: no error
591
+        :statuscode 500: server error
592
+
593
+
594
+Inspect an image
595
+****************
596
+
597
+.. http:get:: /images/(name)/json
598
+
599
+	Return low-level information on the image ``name``
600
+
601
+	**Example request**:
602
+
603
+	.. sourcecode:: http
604
+
605
+	   GET /images/base/json HTTP/1.1
606
+
607
+	**Example response**:
608
+
609
+        .. sourcecode:: http
610
+
611
+           HTTP/1.1 200 OK
612
+	   Content-Type: application/json
613
+
614
+	   {
615
+		"id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
616
+		"parent":"27cf784147099545",
617
+		"created":"2013-03-23T22:24:18.818426-07:00",
618
+		"container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
619
+		"container_config":
620
+			{
621
+				"Hostname":"",
622
+				"User":"",
623
+				"Memory":0,
624
+				"MemorySwap":0,
625
+				"AttachStdin":false,
626
+				"AttachStdout":false,
627
+				"AttachStderr":false,
628
+				"PortSpecs":null,
629
+				"Tty":true,
630
+				"OpenStdin":true,
631
+				"StdinOnce":false,
632
+				"Env":null,
633
+				"Cmd": ["/bin/bash"]
634
+				,"Dns":null,
635
+				"Image":"base",
636
+				"Volumes":null,
637
+				"VolumesFrom":""
638
+			}
639
+	   }
640
+
641
+	:statuscode 200: no error
642
+	:statuscode 404: no such image
643
+        :statuscode 500: server error
644
+
645
+
646
+Get the history of an image
647
+***************************
648
+
649
+.. http:get:: /images/(name)/history
650
+
651
+        Return the history of the image ``name``
652
+
653
+        **Example request**:
654
+
655
+        .. sourcecode:: http
656
+
657
+           GET /images/base/history HTTP/1.1
658
+
659
+        **Example response**:
660
+
661
+        .. sourcecode:: http
662
+
663
+           HTTP/1.1 200 OK
664
+	   Content-Type: application/json
665
+
666
+	   [
667
+		{
668
+			"Id":"b750fe79269d",
669
+			"Created":1364102658,
670
+			"CreatedBy":"/bin/bash"
671
+		},
672
+		{
673
+			"Id":"27cf78414709",
674
+			"Created":1364068391,
675
+			"CreatedBy":""
676
+		}
677
+	   ]
678
+
679
+        :statuscode 200: no error
680
+        :statuscode 404: no such image
681
+        :statuscode 500: server error
682
+
683
+
684
+Push an image on the registry
685
+*****************************
686
+
687
+.. http:post:: /images/(name)/push
688
+
689
+	Push the image ``name`` on the registry
690
+
691
+	 **Example request**:
692
+
693
+	 .. sourcecode:: http
694
+
695
+	    POST /images/test/push HTTP/1.1
696
+
697
+	 **Example response**:
698
+
699
+        .. sourcecode:: http
700
+
701
+           HTTP/1.1 200 OK
702
+	   Content-Type: application/vnd.docker.raw-stream
703
+
704
+	   {{ STREAM }}
705
+
706
+	:query registry: the registry you wan to push, optional
707
+	:statuscode 200: no error
708
+        :statuscode 404: no such image
709
+        :statuscode 500: server error
710
+
711
+
712
+Tag an image into a repository
713
+******************************
714
+
715
+.. http:post:: /images/(name)/tag
716
+
717
+	Tag the image ``name`` into a repository
718
+
719
+        **Example request**:
720
+
721
+        .. sourcecode:: http
722
+			
723
+	   POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1
724
+
725
+	**Example response**:
726
+
727
+        .. sourcecode:: http
728
+
729
+           HTTP/1.1 200 OK
730
+
731
+	:query repo: The repository to tag in
732
+	:query force: 1/True/true or 0/False/false, default false
733
+	:statuscode 200: no error
734
+	:statuscode 400: bad parameter
735
+	:statuscode 404: no such image
736
+        :statuscode 500: server error
737
+
738
+
739
+Remove an image
740
+***************
741
+
742
+.. http:delete:: /images/(name)
743
+
744
+	Remove the image ``name`` from the filesystem 
745
+	
746
+	**Example request**:
747
+
748
+	.. sourcecode:: http
749
+
750
+	   DELETE /images/test HTTP/1.1
751
+
752
+	**Example response**:
753
+
754
+        .. sourcecode:: http
755
+
756
+           HTTP/1.1 204 OK
757
+
758
+	:statuscode 204: no error
759
+        :statuscode 404: no such image
760
+        :statuscode 500: server error
761
+
762
+
763
+Search images
764
+*************
765
+
766
+.. http:get:: /images/search
767
+
768
+	Search for an image in the docker index
769
+	
770
+	**Example request**:
771
+
772
+        .. sourcecode:: http
773
+
774
+           GET /images/search?term=sshd HTTP/1.1
775
+
776
+	**Example response**:
777
+
778
+	.. sourcecode:: http
779
+
780
+	   HTTP/1.1 200 OK
781
+	   Content-Type: application/json
782
+	   
783
+	   [
784
+		{
785
+			"Name":"cespare/sshd",
786
+			"Description":""
787
+		},
788
+		{
789
+			"Name":"johnfuller/sshd",
790
+			"Description":""
791
+		},
792
+		{
793
+			"Name":"dhrp/mongodb-sshd",
794
+			"Description":""
795
+		}
796
+	   ]
797
+
798
+	   :query term: term to search
799
+	   :statuscode 200: no error
800
+	   :statuscode 500: server error
801
+
802
+
803
+2.3 Misc
804
+--------
805
+
806
+Build an image from Dockerfile via stdin
807
+****************************************
808
+
809
+.. http:post:: /build
810
+
811
+	Build an image from Dockerfile via stdin
812
+
813
+	**Example request**:
814
+
815
+        .. sourcecode:: http
816
+
817
+           POST /build HTTP/1.1
818
+	   
819
+	   {{ STREAM }}
820
+
821
+	**Example response**:
822
+
823
+        .. sourcecode:: http
824
+
825
+           HTTP/1.1 200 OK
826
+	   
827
+	   {{ STREAM }}
828
+
829
+	:query t: tag to be applied to the resulting image in case of success
830
+	:statuscode 200: no error
831
+        :statuscode 500: server error
832
+
833
+
834
+Get default username and email
835
+******************************
836
+
837
+.. http:get:: /auth
838
+
839
+	Get the default username and email
840
+
841
+	**Example request**:
842
+
843
+        .. sourcecode:: http
844
+
845
+           GET /auth HTTP/1.1
846
+
847
+        **Example response**:
848
+
849
+        .. sourcecode:: http
850
+
851
+           HTTP/1.1 200 OK
852
+	   Content-Type: application/json
853
+
854
+	   {
855
+		"username":"hannibal",
856
+		"email":"hannibal@a-team.com"
857
+	   }
858
+
859
+        :statuscode 200: no error
860
+        :statuscode 500: server error
861
+
862
+
863
+Check auth configuration and store it
864
+*************************************
865
+
866
+.. http:post:: /auth
867
+
868
+        Get the default username and email
869
+
870
+        **Example request**:
871
+
872
+        .. sourcecode:: http
873
+
874
+           POST /auth HTTP/1.1
875
+	   Content-Type: application/json
876
+
877
+	   {
878
+		"username":"hannibal",
879
+		"password:"xxxx",
880
+		"email":"hannibal@a-team.com"
881
+	   }
882
+
883
+        **Example response**:
884
+
885
+        .. sourcecode:: http
886
+
887
+           HTTP/1.1 200 OK
888
+
889
+        :statuscode 200: no error
890
+        :statuscode 204: no error
891
+        :statuscode 500: server error
892
+
893
+
894
+Display system-wide information
895
+*******************************
896
+
897
+.. http:get:: /info
898
+
899
+	Display system-wide information
900
+	
901
+	**Example request**:
902
+
903
+        .. sourcecode:: http
904
+
905
+           GET /info HTTP/1.1
906
+
907
+        **Example response**:
908
+
909
+        .. sourcecode:: http
910
+
911
+           HTTP/1.1 200 OK
912
+	   Content-Type: application/json
913
+
914
+	   {
915
+		"Containers":11,
916
+		"Images":16,
917
+		"Debug":false,
918
+		"NFd": 11,
919
+		"NGoroutines":21,
920
+		"MemoryLimit":true,
921
+		"SwapLimit":false
922
+	   }
923
+
924
+        :statuscode 200: no error
925
+        :statuscode 500: server error
926
+
927
+
928
+Show the docker version information
929
+***********************************
930
+
931
+.. http:get:: /version
932
+
933
+	Show the docker version information
934
+
935
+	**Example request**:
936
+
937
+        .. sourcecode:: http
938
+
939
+           GET /version HTTP/1.1
940
+
941
+        **Example response**:
942
+
943
+        .. sourcecode:: http
944
+
945
+           HTTP/1.1 200 OK
946
+	   Content-Type: application/json
947
+
948
+	   {
949
+		"Version":"0.2.2",
950
+		"GitCommit":"5a2a5cc+CHANGES",
951
+		"GoVersion":"go1.0.3"
952
+	   }
953
+
954
+        :statuscode 200: no error
955
+	:statuscode 500: server error
956
+
957
+
958
+Create a new image from a container's changes
959
+*********************************************
960
+
961
+.. http:post:: /commit
962
+
963
+	Create a new image from a container's changes
964
+
965
+	**Example request**:
966
+
967
+        .. sourcecode:: http
968
+
969
+           POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
970
+
971
+        **Example response**:
972
+
973
+        .. sourcecode:: http
974
+
975
+           HTTP/1.1 201 OK
976
+	   Content-Type: application/vnd.docker.raw-stream
977
+
978
+           {"Id":"596069db4bf5"}
979
+
980
+	:query container: source container
981
+	:query repo: repository
982
+	:query tag: tag
983
+	:query m: commit message
984
+	:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
985
+	:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
986
+        :statuscode 201: no error
987
+	:statuscode 404: no such container
988
+        :statuscode 500: server error
989
+
990
+
991
+3. Going further
992
+================
993
+
994
+3.1 Inside 'docker run'
995
+-----------------------
996
+
997
+Here are the steps of 'docker run' :
998
+
999
+* Create the container
1000
+* If the status code is 404, it means the image doesn't exists:
1001
+        * Try to pull it
1002
+        * Then retry to create the container
1003
+* Start the container
1004
+* If you are not in detached mode:
1005
+        * Attach to the container, using logs=1 (to have stdout and stderr from the container's start) and stream=1
1006
+* If in detached mode or only stdin is attached:
1007
+	* Display the container's id
1008
+
1009
+
1010
+3.2 Hijacking
1011
+-------------
1012
+
1013
+In this first version of the API, some of the endpoints, like /attach, /pull or /push uses hijacking to transport stdin,
1014
+stdout and stderr on the same socket. This might change in the future.
0 1015
new file mode 100644
... ...
@@ -0,0 +1,1026 @@
0
+
1
+:title: Remote API v1.1
2
+:description: API Documentation for Docker
3
+:keywords: API, Docker, rcli, REST, documentation
4
+
5
+======================
6
+Docker Remote API v1.1
7
+======================
8
+
9
+.. contents:: Table of Contents
10
+
11
+1. Brief introduction
12
+=====================
13
+
14
+- The Remote API is replacing rcli
15
+- Default port in the docker deamon is 4243 
16
+- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
17
+
18
+2. Endpoints
19
+============
20
+
21
+2.1 Containers
22
+--------------
23
+
24
+List containers
25
+***************
26
+
27
+.. http:get:: /containers/json
28
+
29
+	List containers
30
+
31
+	**Example request**:
32
+
33
+	.. sourcecode:: http
34
+
35
+	   GET /containers/json?all=1&before=8dfafdbc3a40 HTTP/1.1
36
+	   
37
+	**Example response**:
38
+
39
+	.. sourcecode:: http
40
+
41
+	   HTTP/1.1 200 OK
42
+	   Content-Type: application/json
43
+	   
44
+	   [
45
+		{
46
+			"Id": "8dfafdbc3a40",
47
+			"Image": "base:latest",
48
+			"Command": "echo 1",
49
+			"Created": 1367854155,
50
+			"Status": "Exit 0"
51
+		},
52
+		{
53
+			"Id": "9cd87474be90",
54
+			"Image": "base:latest",
55
+			"Command": "echo 222222",
56
+			"Created": 1367854155,
57
+			"Status": "Exit 0"
58
+		},
59
+		{
60
+			"Id": "3176a2479c92",
61
+			"Image": "base:latest",
62
+			"Command": "echo 3333333333333333",
63
+			"Created": 1367854154,
64
+			"Status": "Exit 0"
65
+		},
66
+		{
67
+			"Id": "4cb07b47f9fb",
68
+			"Image": "base:latest",
69
+			"Command": "echo 444444444444444444444444444444444",
70
+			"Created": 1367854152,
71
+			"Status": "Exit 0"
72
+		}
73
+	   ]
74
+ 
75
+	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
76
+	:query limit: Show ``limit`` last created containers, include non-running ones.
77
+	:query since: Show only containers created since Id, include non-running ones.
78
+	:query before: Show only containers created before Id, include non-running ones.
79
+	:statuscode 200: no error
80
+	:statuscode 400: bad parameter
81
+	:statuscode 500: server error
82
+
83
+
84
+Create a container
85
+******************
86
+
87
+.. http:post:: /containers/create
88
+
89
+	Create a container
90
+
91
+	**Example request**:
92
+
93
+	.. sourcecode:: http
94
+
95
+	   POST /containers/create HTTP/1.1
96
+	   Content-Type: application/json
97
+
98
+	   {
99
+		"Hostname":"",
100
+		"User":"",
101
+		"Memory":0,
102
+		"MemorySwap":0,
103
+		"AttachStdin":false,
104
+		"AttachStdout":true,
105
+		"AttachStderr":true,
106
+		"PortSpecs":null,
107
+		"Tty":false,
108
+		"OpenStdin":false,
109
+		"StdinOnce":false,
110
+		"Env":null,
111
+		"Cmd":[
112
+			"date"
113
+		],
114
+		"Dns":null,
115
+		"Image":"base",
116
+		"Volumes":{},
117
+		"VolumesFrom":""
118
+	   }
119
+	   
120
+	**Example response**:
121
+
122
+	.. sourcecode:: http
123
+
124
+	   HTTP/1.1 201 OK
125
+	   Content-Type: application/json
126
+
127
+	   {
128
+		"Id":"e90e34656806"
129
+		"Warnings":[]
130
+	   }
131
+	
132
+	:jsonparam config: the container's configuration
133
+	:statuscode 201: no error
134
+	:statuscode 404: no such container
135
+	:statuscode 406: impossible to attach (container not running)
136
+	:statuscode 500: server error
137
+
138
+
139
+Inspect a container
140
+*******************
141
+
142
+.. http:get:: /containers/(id)/json
143
+
144
+	Return low-level information on the container ``id``
145
+
146
+	**Example request**:
147
+
148
+	.. sourcecode:: http
149
+
150
+	   GET /containers/4fa6e0f0c678/json HTTP/1.1
151
+	   
152
+	**Example response**:
153
+
154
+	.. sourcecode:: http
155
+
156
+	   HTTP/1.1 200 OK
157
+	   Content-Type: application/json
158
+
159
+	   {
160
+			"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
161
+			"Created": "2013-05-07T14:51:42.041847+02:00",
162
+			"Path": "date",
163
+			"Args": [],
164
+			"Config": {
165
+				"Hostname": "4fa6e0f0c678",
166
+				"User": "",
167
+				"Memory": 0,
168
+				"MemorySwap": 0,
169
+				"AttachStdin": false,
170
+				"AttachStdout": true,
171
+				"AttachStderr": true,
172
+				"PortSpecs": null,
173
+				"Tty": false,
174
+				"OpenStdin": false,
175
+				"StdinOnce": false,
176
+				"Env": null,
177
+				"Cmd": [
178
+					"date"
179
+				],
180
+				"Dns": null,
181
+				"Image": "base",
182
+				"Volumes": {},
183
+				"VolumesFrom": ""
184
+			},
185
+			"State": {
186
+				"Running": false,
187
+				"Pid": 0,
188
+				"ExitCode": 0,
189
+				"StartedAt": "2013-05-07T14:51:42.087658+02:01360",
190
+				"Ghost": false
191
+			},
192
+			"Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
193
+			"NetworkSettings": {
194
+				"IpAddress": "",
195
+				"IpPrefixLen": 0,
196
+				"Gateway": "",
197
+				"Bridge": "",
198
+				"PortMapping": null
199
+			},
200
+			"SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
201
+			"ResolvConfPath": "/etc/resolv.conf",
202
+			"Volumes": {}
203
+	   }
204
+
205
+	:statuscode 200: no error
206
+	:statuscode 404: no such container
207
+	:statuscode 500: server error
208
+
209
+
210
+Inspect changes on a container's filesystem
211
+*******************************************
212
+
213
+.. http:get:: /containers/(id)/changes
214
+
215
+	Inspect changes on container ``id`` 's filesystem
216
+
217
+	**Example request**:
218
+
219
+	.. sourcecode:: http
220
+
221
+	   GET /containers/4fa6e0f0c678/changes HTTP/1.1
222
+
223
+	   
224
+	**Example response**:
225
+
226
+	.. sourcecode:: http
227
+
228
+	   HTTP/1.1 200 OK
229
+	   Content-Type: application/json
230
+	   
231
+	   [
232
+		{
233
+			"Path":"/dev",
234
+			"Kind":0
235
+		},
236
+		{
237
+			"Path":"/dev/kmsg",
238
+			"Kind":1
239
+		},
240
+		{
241
+			"Path":"/test",
242
+			"Kind":1
243
+		}
244
+	   ]
245
+
246
+	:statuscode 200: no error
247
+	:statuscode 404: no such container
248
+	:statuscode 500: server error
249
+
250
+
251
+Export a container
252
+******************
253
+
254
+.. http:get:: /containers/(id)/export
255
+
256
+	Export the contents of container ``id``
257
+
258
+	**Example request**:
259
+
260
+	.. sourcecode:: http
261
+
262
+	   GET /containers/4fa6e0f0c678/export HTTP/1.1
263
+
264
+	   
265
+	**Example response**:
266
+
267
+	.. sourcecode:: http
268
+
269
+	   HTTP/1.1 200 OK
270
+	   Content-Type: application/octet-stream
271
+	   
272
+	   {{ STREAM }}
273
+
274
+	:statuscode 200: no error
275
+	:statuscode 404: no such container
276
+	:statuscode 500: server error
277
+
278
+
279
+Start a container
280
+*****************
281
+
282
+.. http:post:: /containers/(id)/start
283
+
284
+	Start the container ``id``
285
+
286
+	**Example request**:
287
+
288
+	.. sourcecode:: http
289
+
290
+	   POST /containers/e90e34656806/start HTTP/1.1
291
+	   
292
+	**Example response**:
293
+
294
+	.. sourcecode:: http
295
+
296
+	   HTTP/1.1 200 OK
297
+	   	
298
+	:statuscode 200: no error
299
+	:statuscode 404: no such container
300
+	:statuscode 500: server error
301
+
302
+
303
+Stop a contaier
304
+***************
305
+
306
+.. http:post:: /containers/(id)/stop
307
+
308
+	Stop the container ``id``
309
+
310
+	**Example request**:
311
+
312
+	.. sourcecode:: http
313
+
314
+	   POST /containers/e90e34656806/stop?t=5 HTTP/1.1
315
+	   
316
+	**Example response**:
317
+
318
+	.. sourcecode:: http
319
+
320
+	   HTTP/1.1 204 OK
321
+	   	
322
+	:query t: number of seconds to wait before killing the container
323
+	:statuscode 204: no error
324
+	:statuscode 404: no such container
325
+	:statuscode 500: server error
326
+
327
+
328
+Restart a container
329
+*******************
330
+
331
+.. http:post:: /containers/(id)/restart
332
+
333
+	Restart the container ``id``
334
+
335
+	**Example request**:
336
+
337
+	.. sourcecode:: http
338
+
339
+	   POST /containers/e90e34656806/restart?t=5 HTTP/1.1
340
+	   
341
+	**Example response**:
342
+
343
+	.. sourcecode:: http
344
+
345
+	   HTTP/1.1 204 OK
346
+	   	
347
+	:query t: number of seconds to wait before killing the container
348
+	:statuscode 204: no error
349
+	:statuscode 404: no such container
350
+	:statuscode 500: server error
351
+
352
+
353
+Kill a container
354
+****************
355
+
356
+.. http:post:: /containers/(id)/kill
357
+
358
+	Kill the container ``id``
359
+
360
+	**Example request**:
361
+
362
+	.. sourcecode:: http
363
+
364
+	   POST /containers/e90e34656806/kill HTTP/1.1
365
+	   
366
+	**Example response**:
367
+
368
+	.. sourcecode:: http
369
+
370
+	   HTTP/1.1 204 OK
371
+	   	
372
+	:statuscode 204: no error
373
+	:statuscode 404: no such container
374
+	:statuscode 500: server error
375
+
376
+
377
+Attach to a container
378
+*********************
379
+
380
+.. http:post:: /containers/(id)/attach
381
+
382
+	Attach to the container ``id``
383
+
384
+	**Example request**:
385
+
386
+	.. sourcecode:: http
387
+
388
+	   POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1
389
+	   
390
+	**Example response**:
391
+
392
+	.. sourcecode:: http
393
+
394
+	   HTTP/1.1 200 OK
395
+	   Content-Type: application/vnd.docker.raw-stream
396
+
397
+	   {{ STREAM }}
398
+	   	
399
+	:query logs: 1/True/true or 0/False/false, return logs. Default false
400
+	:query stream: 1/True/true or 0/False/false, return stream. Default false
401
+	:query stdin: 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false
402
+	:query stdout: 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false
403
+	:query stderr: 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false
404
+	:statuscode 200: no error
405
+	:statuscode 400: bad parameter
406
+	:statuscode 404: no such container
407
+	:statuscode 500: server error
408
+
409
+
410
+Wait a container
411
+****************
412
+
413
+.. http:post:: /containers/(id)/wait
414
+
415
+	Block until container ``id`` stops, then returns the exit code
416
+
417
+	**Example request**:
418
+
419
+	.. sourcecode:: http
420
+
421
+	   POST /containers/16253994b7c4/wait HTTP/1.1
422
+	   
423
+	**Example response**:
424
+
425
+	.. sourcecode:: http
426
+
427
+	   HTTP/1.1 200 OK
428
+	   Content-Type: application/json
429
+
430
+	   {"StatusCode":0}
431
+	   	
432
+	:statuscode 200: no error
433
+	:statuscode 404: no such container
434
+	:statuscode 500: server error
435
+
436
+
437
+Remove a container
438
+*******************
439
+
440
+.. http:delete:: /containers/(id)
441
+
442
+	Remove the container ``id`` from the filesystem
443
+
444
+	**Example request**:
445
+
446
+        .. sourcecode:: http
447
+
448
+           DELETE /containers/16253994b7c4?v=1 HTTP/1.1
449
+
450
+        **Example response**:
451
+
452
+        .. sourcecode:: http
453
+
454
+	   HTTP/1.1 204 OK
455
+
456
+	:query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false
457
+        :statuscode 204: no error
458
+	:statuscode 400: bad parameter
459
+        :statuscode 404: no such container
460
+        :statuscode 500: server error
461
+
462
+
463
+2.2 Images
464
+----------
465
+
466
+List Images
467
+***********
468
+
469
+.. http:get:: /images/(format)
470
+
471
+	List images ``format`` could be json or viz (json default)
472
+
473
+	**Example request**:
474
+
475
+	.. sourcecode:: http
476
+
477
+	   GET /images/json?all=0 HTTP/1.1
478
+
479
+	**Example response**:
480
+
481
+	.. sourcecode:: http
482
+
483
+	   HTTP/1.1 200 OK
484
+	   Content-Type: application/json
485
+	   
486
+	   [
487
+		{
488
+			"Repository":"base",
489
+			"Tag":"ubuntu-12.10",
490
+			"Id":"b750fe79269d",
491
+			"Created":1364102658
492
+		},
493
+		{
494
+			"Repository":"base",
495
+			"Tag":"ubuntu-quantal",
496
+			"Id":"b750fe79269d",
497
+			"Created":1364102658
498
+		}
499
+	   ]
500
+
501
+
502
+	**Example request**:
503
+
504
+	.. sourcecode:: http
505
+
506
+	   GET /images/viz HTTP/1.1
507
+
508
+	**Example response**:
509
+
510
+	.. sourcecode:: http
511
+
512
+	   HTTP/1.1 200 OK
513
+	   Content-Type: text/plain
514
+
515
+	   digraph docker {
516
+	   "d82cbacda43a" -> "074be284591f"
517
+	   "1496068ca813" -> "08306dc45919"
518
+	   "08306dc45919" -> "0e7893146ac2"
519
+	   "b750fe79269d" -> "1496068ca813"
520
+	   base -> "27cf78414709" [style=invis]
521
+	   "f71189fff3de" -> "9a33b36209ed"
522
+	   "27cf78414709" -> "b750fe79269d"
523
+	   "0e7893146ac2" -> "d6434d954665"
524
+	   "d6434d954665" -> "d82cbacda43a"
525
+	   base -> "e9aa60c60128" [style=invis]
526
+	   "074be284591f" -> "f71189fff3de"
527
+	   "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
528
+	   "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
529
+	   "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
530
+	   base [style=invisible]
531
+	   }
532
+ 
533
+	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
534
+	:statuscode 200: no error
535
+	:statuscode 400: bad parameter
536
+	:statuscode 500: server error
537
+
538
+
539
+Create an image
540
+***************
541
+
542
+.. http:post:: /images/create
543
+
544
+	Create an image, either by pull it from the registry or by importing it
545
+
546
+	**Example request**:
547
+
548
+        .. sourcecode:: http
549
+
550
+           POST /images/create?fromImage=base HTTP/1.1
551
+
552
+        **Example response**:
553
+
554
+        .. sourcecode:: http
555
+
556
+           HTTP/1.1 200 OK
557
+	   Content-Type: application/json
558
+
559
+	   {"status":"Pulling..."}
560
+	   {"status":"Pulling", "progress":"1/? (n/a)"}
561
+	   {"error":"Invalid..."}
562
+	   ...
563
+
564
+        :query fromImage: name of the image to pull
565
+	:query fromSrc: source to import, - means stdin
566
+        :query repo: repository
567
+	:query tag: tag
568
+	:query registry: the registry to pull from
569
+        :statuscode 200: no error
570
+        :statuscode 500: server error
571
+
572
+
573
+Insert a file in a image
574
+************************
575
+
576
+.. http:post:: /images/(name)/insert
577
+
578
+	Insert a file from ``url`` in the image ``name`` at ``path``
579
+
580
+	**Example request**:
581
+
582
+        .. sourcecode:: http
583
+
584
+           POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
585
+
586
+	**Example response**:
587
+
588
+        .. sourcecode:: http
589
+
590
+           HTTP/1.1 200 OK
591
+	   Content-Type: application/json
592
+
593
+	   {"status":"Inserting..."}
594
+	   {"status":"Inserting", "progress":"1/? (n/a)"}
595
+	   {"error":"Invalid..."}
596
+	   ...
597
+
598
+	:statuscode 200: no error
599
+        :statuscode 500: server error
600
+
601
+
602
+Inspect an image
603
+****************
604
+
605
+.. http:get:: /images/(name)/json
606
+
607
+	Return low-level information on the image ``name``
608
+
609
+	**Example request**:
610
+
611
+	.. sourcecode:: http
612
+
613
+	   GET /images/base/json HTTP/1.1
614
+
615
+	**Example response**:
616
+
617
+        .. sourcecode:: http
618
+
619
+           HTTP/1.1 200 OK
620
+	   Content-Type: application/json
621
+
622
+	   {
623
+		"id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
624
+		"parent":"27cf784147099545",
625
+		"created":"2013-03-23T22:24:18.818426-07:00",
626
+		"container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
627
+		"container_config":
628
+			{
629
+				"Hostname":"",
630
+				"User":"",
631
+				"Memory":0,
632
+				"MemorySwap":0,
633
+				"AttachStdin":false,
634
+				"AttachStdout":false,
635
+				"AttachStderr":false,
636
+				"PortSpecs":null,
637
+				"Tty":true,
638
+				"OpenStdin":true,
639
+				"StdinOnce":false,
640
+				"Env":null,
641
+				"Cmd": ["/bin/bash"]
642
+				,"Dns":null,
643
+				"Image":"base",
644
+				"Volumes":null,
645
+				"VolumesFrom":""
646
+			}
647
+	   }
648
+
649
+	:statuscode 200: no error
650
+	:statuscode 404: no such image
651
+        :statuscode 500: server error
652
+
653
+
654
+Get the history of an image
655
+***************************
656
+
657
+.. http:get:: /images/(name)/history
658
+
659
+        Return the history of the image ``name``
660
+
661
+        **Example request**:
662
+
663
+        .. sourcecode:: http
664
+
665
+           GET /images/base/history HTTP/1.1
666
+
667
+        **Example response**:
668
+
669
+        .. sourcecode:: http
670
+
671
+           HTTP/1.1 200 OK
672
+	   Content-Type: application/json
673
+
674
+	   [
675
+		{
676
+			"Id":"b750fe79269d",
677
+			"Created":1364102658,
678
+			"CreatedBy":"/bin/bash"
679
+		},
680
+		{
681
+			"Id":"27cf78414709",
682
+			"Created":1364068391,
683
+			"CreatedBy":""
684
+		}
685
+	   ]
686
+
687
+        :statuscode 200: no error
688
+        :statuscode 404: no such image
689
+        :statuscode 500: server error
690
+
691
+
692
+Push an image on the registry
693
+*****************************
694
+
695
+.. http:post:: /images/(name)/push
696
+
697
+	Push the image ``name`` on the registry
698
+
699
+	 **Example request**:
700
+
701
+	 .. sourcecode:: http
702
+
703
+	    POST /images/test/push HTTP/1.1
704
+
705
+	 **Example response**:
706
+
707
+        .. sourcecode:: http
708
+
709
+           HTTP/1.1 200 OK
710
+	   Content-Type: application/json
711
+
712
+	   {"status":"Pushing..."}
713
+	   {"status":"Pushing", "progress":"1/? (n/a)"}
714
+	   {"error":"Invalid..."}
715
+	   ...
716
+
717
+	:query registry: the registry you wan to push, optional
718
+	:statuscode 200: no error
719
+        :statuscode 404: no such image
720
+        :statuscode 500: server error
721
+
722
+
723
+Tag an image into a repository
724
+******************************
725
+
726
+.. http:post:: /images/(name)/tag
727
+
728
+	Tag the image ``name`` into a repository
729
+
730
+        **Example request**:
731
+
732
+        .. sourcecode:: http
733
+			
734
+	   POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1
735
+
736
+	**Example response**:
737
+
738
+        .. sourcecode:: http
739
+
740
+           HTTP/1.1 200 OK
741
+
742
+	:query repo: The repository to tag in
743
+	:query force: 1/True/true or 0/False/false, default false
744
+	:statuscode 200: no error
745
+	:statuscode 400: bad parameter
746
+	:statuscode 404: no such image
747
+	:statuscode 409: conflict
748
+        :statuscode 500: server error
749
+
750
+
751
+Remove an image
752
+***************
753
+
754
+.. http:delete:: /images/(name)
755
+
756
+	Remove the image ``name`` from the filesystem 
757
+	
758
+	**Example request**:
759
+
760
+	.. sourcecode:: http
761
+
762
+	   DELETE /images/test HTTP/1.1
763
+
764
+	**Example response**:
765
+
766
+        .. sourcecode:: http
767
+
768
+           HTTP/1.1 204 OK
769
+
770
+	:statuscode 204: no error
771
+        :statuscode 404: no such image
772
+        :statuscode 500: server error
773
+
774
+
775
+Search images
776
+*************
777
+
778
+.. http:get:: /images/search
779
+
780
+	Search for an image in the docker index
781
+	
782
+	**Example request**:
783
+
784
+        .. sourcecode:: http
785
+
786
+           GET /images/search?term=sshd HTTP/1.1
787
+
788
+	**Example response**:
789
+
790
+	.. sourcecode:: http
791
+
792
+	   HTTP/1.1 200 OK
793
+	   Content-Type: application/json
794
+	   
795
+	   [
796
+		{
797
+			"Name":"cespare/sshd",
798
+			"Description":""
799
+		},
800
+		{
801
+			"Name":"johnfuller/sshd",
802
+			"Description":""
803
+		},
804
+		{
805
+			"Name":"dhrp/mongodb-sshd",
806
+			"Description":""
807
+		}
808
+	   ]
809
+
810
+	   :query term: term to search
811
+	   :statuscode 200: no error
812
+	   :statuscode 500: server error
813
+
814
+
815
+2.3 Misc
816
+--------
817
+
818
+Build an image from Dockerfile via stdin
819
+****************************************
820
+
821
+.. http:post:: /build
822
+
823
+	Build an image from Dockerfile via stdin
824
+
825
+	**Example request**:
826
+
827
+        .. sourcecode:: http
828
+
829
+           POST /build HTTP/1.1
830
+	   
831
+	   {{ STREAM }}
832
+
833
+	**Example response**:
834
+
835
+        .. sourcecode:: http
836
+
837
+           HTTP/1.1 200 OK
838
+	   
839
+	   {{ STREAM }}
840
+
841
+	:query t: tag to be applied to the resulting image in case of success
842
+	:statuscode 200: no error
843
+        :statuscode 500: server error
844
+
845
+
846
+Get default username and email
847
+******************************
848
+
849
+.. http:get:: /auth
850
+
851
+	Get the default username and email
852
+
853
+	**Example request**:
854
+
855
+        .. sourcecode:: http
856
+
857
+           GET /auth HTTP/1.1
858
+
859
+        **Example response**:
860
+
861
+        .. sourcecode:: http
862
+
863
+           HTTP/1.1 200 OK
864
+	   Content-Type: application/json
865
+
866
+	   {
867
+		"username":"hannibal",
868
+		"email":"hannibal@a-team.com"
869
+	   }
870
+
871
+        :statuscode 200: no error
872
+        :statuscode 500: server error
873
+
874
+
875
+Check auth configuration and store it
876
+*************************************
877
+
878
+.. http:post:: /auth
879
+
880
+        Get the default username and email
881
+
882
+        **Example request**:
883
+
884
+        .. sourcecode:: http
885
+
886
+           POST /auth HTTP/1.1
887
+	   Content-Type: application/json
888
+
889
+	   {
890
+		"username":"hannibal",
891
+		"password:"xxxx",
892
+		"email":"hannibal@a-team.com"
893
+	   }
894
+
895
+        **Example response**:
896
+
897
+        .. sourcecode:: http
898
+
899
+           HTTP/1.1 200 OK
900
+
901
+        :statuscode 200: no error
902
+        :statuscode 204: no error
903
+        :statuscode 500: server error
904
+
905
+
906
+Display system-wide information
907
+*******************************
908
+
909
+.. http:get:: /info
910
+
911
+	Display system-wide information
912
+	
913
+	**Example request**:
914
+
915
+        .. sourcecode:: http
916
+
917
+           GET /info HTTP/1.1
918
+
919
+        **Example response**:
920
+
921
+        .. sourcecode:: http
922
+
923
+           HTTP/1.1 200 OK
924
+	   Content-Type: application/json
925
+
926
+	   {
927
+		"Containers":11,
928
+		"Images":16,
929
+		"Debug":false,
930
+		"NFd": 11,
931
+		"NGoroutines":21,
932
+		"MemoryLimit":true,
933
+		"SwapLimit":false
934
+	   }
935
+
936
+        :statuscode 200: no error
937
+        :statuscode 500: server error
938
+
939
+
940
+Show the docker version information
941
+***********************************
942
+
943
+.. http:get:: /version
944
+
945
+	Show the docker version information
946
+
947
+	**Example request**:
948
+
949
+        .. sourcecode:: http
950
+
951
+           GET /version HTTP/1.1
952
+
953
+        **Example response**:
954
+
955
+        .. sourcecode:: http
956
+
957
+           HTTP/1.1 200 OK
958
+	   Content-Type: application/json
959
+
960
+	   {
961
+		"Version":"0.2.2",
962
+		"GitCommit":"5a2a5cc+CHANGES",
963
+		"GoVersion":"go1.0.3"
964
+	   }
965
+
966
+        :statuscode 200: no error
967
+	:statuscode 500: server error
968
+
969
+
970
+Create a new image from a container's changes
971
+*********************************************
972
+
973
+.. http:post:: /commit
974
+
975
+	Create a new image from a container's changes
976
+
977
+	**Example request**:
978
+
979
+        .. sourcecode:: http
980
+
981
+           POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
982
+
983
+        **Example response**:
984
+
985
+        .. sourcecode:: http
986
+
987
+           HTTP/1.1 201 OK
988
+	   Content-Type: application/vnd.docker.raw-stream
989
+
990
+           {"Id":"596069db4bf5"}
991
+
992
+	:query container: source container
993
+	:query repo: repository
994
+	:query tag: tag
995
+	:query m: commit message
996
+	:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
997
+	:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
998
+        :statuscode 201: no error
999
+	:statuscode 404: no such container
1000
+        :statuscode 500: server error
1001
+
1002
+
1003
+3. Going further
1004
+================
1005
+
1006
+3.1 Inside 'docker run'
1007
+-----------------------
1008
+
1009
+Here are the steps of 'docker run' :
1010
+
1011
+* Create the container
1012
+* If the status code is 404, it means the image doesn't exists:
1013
+        * Try to pull it
1014
+        * Then retry to create the container
1015
+* Start the container
1016
+* If you are not in detached mode:
1017
+        * Attach to the container, using logs=1 (to have stdout and stderr from the container's start) and stream=1
1018
+* If in detached mode or only stdin is attached:
1019
+	* Display the container's id
1020
+
1021
+
1022
+3.2 Hijacking
1023
+-------------
1024
+
1025
+In this version of the API, /attach uses hijacking to transport stdin, stdout and stderr on the same socket. This might change in the future.
0 1026
new file mode 100644
... ...
@@ -0,0 +1,1013 @@
0
+:title: Remote API v1.2
1
+:description: API Documentation for Docker
2
+:keywords: API, Docker, rcli, REST, documentation
3
+
4
+======================
5
+Docker Remote API v1.2
6
+======================
7
+
8
+.. contents:: Table of Contents
9
+
10
+1. Brief introduction
11
+=====================
12
+
13
+- The Remote API is replacing rcli
14
+- Default port in the docker deamon is 4243 
15
+- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
16
+
17
+2. Endpoints
18
+============
19
+
20
+2.1 Containers
21
+--------------
22
+
23
+List containers
24
+***************
25
+
26
+.. http:get:: /containers/json
27
+
28
+	List containers
29
+
30
+	**Example request**:
31
+
32
+	.. sourcecode:: http
33
+
34
+	   GET /containers/json?all=1&before=8dfafdbc3a40 HTTP/1.1
35
+	   
36
+	**Example response**:
37
+
38
+	.. sourcecode:: http
39
+
40
+	   HTTP/1.1 200 OK
41
+	   Content-Type: application/json
42
+	   
43
+	   [
44
+		{
45
+			"Id": "8dfafdbc3a40",
46
+			"Image": "base:latest",
47
+			"Command": "echo 1",
48
+			"Created": 1367854155,
49
+			"Status": "Exit 0"
50
+		},
51
+		{
52
+			"Id": "9cd87474be90",
53
+			"Image": "base:latest",
54
+			"Command": "echo 222222",
55
+			"Created": 1367854155,
56
+			"Status": "Exit 0"
57
+		},
58
+		{
59
+			"Id": "3176a2479c92",
60
+			"Image": "base:latest",
61
+			"Command": "echo 3333333333333333",
62
+			"Created": 1367854154,
63
+			"Status": "Exit 0"
64
+		},
65
+		{
66
+			"Id": "4cb07b47f9fb",
67
+			"Image": "base:latest",
68
+			"Command": "echo 444444444444444444444444444444444",
69
+			"Created": 1367854152,
70
+			"Status": "Exit 0"
71
+		}
72
+	   ]
73
+ 
74
+	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
75
+	:query limit: Show ``limit`` last created containers, include non-running ones.
76
+	:query since: Show only containers created since Id, include non-running ones.
77
+	:query before: Show only containers created before Id, include non-running ones.
78
+	:statuscode 200: no error
79
+	:statuscode 400: bad parameter
80
+	:statuscode 500: server error
81
+
82
+
83
+Create a container
84
+******************
85
+
86
+.. http:post:: /containers/create
87
+
88
+	Create a container
89
+
90
+	**Example request**:
91
+
92
+	.. sourcecode:: http
93
+
94
+	   POST /containers/create HTTP/1.1
95
+	   Content-Type: application/json
96
+
97
+	   {
98
+		"Hostname":"",
99
+		"User":"",
100
+		"Memory":0,
101
+		"MemorySwap":0,
102
+		"AttachStdin":false,
103
+		"AttachStdout":true,
104
+		"AttachStderr":true,
105
+		"PortSpecs":null,
106
+		"Tty":false,
107
+		"OpenStdin":false,
108
+		"StdinOnce":false,
109
+		"Env":null,
110
+		"Cmd":[
111
+			"date"
112
+		],
113
+		"Dns":null,
114
+		"Image":"base",
115
+		"Volumes":{},
116
+		"VolumesFrom":""
117
+	   }
118
+	   
119
+	**Example response**:
120
+
121
+	.. sourcecode:: http
122
+
123
+	   HTTP/1.1 201 OK
124
+	   Content-Type: application/json
125
+
126
+	   {
127
+		"Id":"e90e34656806"
128
+		"Warnings":[]
129
+	   }
130
+	
131
+	:jsonparam config: the container's configuration
132
+	:statuscode 201: no error
133
+	:statuscode 404: no such container
134
+	:statuscode 406: impossible to attach (container not running)
135
+	:statuscode 500: server error
136
+
137
+
138
+Inspect a container
139
+*******************
140
+
141
+.. http:get:: /containers/(id)/json
142
+
143
+	Return low-level information on the container ``id``
144
+
145
+	**Example request**:
146
+
147
+	.. sourcecode:: http
148
+
149
+	   GET /containers/4fa6e0f0c678/json HTTP/1.1
150
+	   
151
+	**Example response**:
152
+
153
+	.. sourcecode:: http
154
+
155
+	   HTTP/1.1 200 OK
156
+	   Content-Type: application/json
157
+
158
+	   {
159
+			"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
160
+			"Created": "2013-05-07T14:51:42.041847+02:00",
161
+			"Path": "date",
162
+			"Args": [],
163
+			"Config": {
164
+				"Hostname": "4fa6e0f0c678",
165
+				"User": "",
166
+				"Memory": 0,
167
+				"MemorySwap": 0,
168
+				"AttachStdin": false,
169
+				"AttachStdout": true,
170
+				"AttachStderr": true,
171
+				"PortSpecs": null,
172
+				"Tty": false,
173
+				"OpenStdin": false,
174
+				"StdinOnce": false,
175
+				"Env": null,
176
+				"Cmd": [
177
+					"date"
178
+				],
179
+				"Dns": null,
180
+				"Image": "base",
181
+				"Volumes": {},
182
+				"VolumesFrom": ""
183
+			},
184
+			"State": {
185
+				"Running": false,
186
+				"Pid": 0,
187
+				"ExitCode": 0,
188
+				"StartedAt": "2013-05-07T14:51:42.087658+02:01360",
189
+				"Ghost": false
190
+			},
191
+			"Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
192
+			"NetworkSettings": {
193
+				"IpAddress": "",
194
+				"IpPrefixLen": 0,
195
+				"Gateway": "",
196
+				"Bridge": "",
197
+				"PortMapping": null
198
+			},
199
+			"SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
200
+			"ResolvConfPath": "/etc/resolv.conf",
201
+			"Volumes": {}
202
+	   }
203
+
204
+	:statuscode 200: no error
205
+	:statuscode 404: no such container
206
+	:statuscode 500: server error
207
+
208
+
209
+Inspect changes on a container's filesystem
210
+*******************************************
211
+
212
+.. http:get:: /containers/(id)/changes
213
+
214
+	Inspect changes on container ``id`` 's filesystem
215
+
216
+	**Example request**:
217
+
218
+	.. sourcecode:: http
219
+
220
+	   GET /containers/4fa6e0f0c678/changes HTTP/1.1
221
+
222
+	   
223
+	**Example response**:
224
+
225
+	.. sourcecode:: http
226
+
227
+	   HTTP/1.1 200 OK
228
+	   Content-Type: application/json
229
+	   
230
+	   [
231
+		{
232
+			"Path":"/dev",
233
+			"Kind":0
234
+		},
235
+		{
236
+			"Path":"/dev/kmsg",
237
+			"Kind":1
238
+		},
239
+		{
240
+			"Path":"/test",
241
+			"Kind":1
242
+		}
243
+	   ]
244
+
245
+	:statuscode 200: no error
246
+	:statuscode 404: no such container
247
+	:statuscode 500: server error
248
+
249
+
250
+Export a container
251
+******************
252
+
253
+.. http:get:: /containers/(id)/export
254
+
255
+	Export the contents of container ``id``
256
+
257
+	**Example request**:
258
+
259
+	.. sourcecode:: http
260
+
261
+	   GET /containers/4fa6e0f0c678/export HTTP/1.1
262
+
263
+	   
264
+	**Example response**:
265
+
266
+	.. sourcecode:: http
267
+
268
+	   HTTP/1.1 200 OK
269
+	   Content-Type: application/octet-stream
270
+	   
271
+	   {{ STREAM }}
272
+
273
+	:statuscode 200: no error
274
+	:statuscode 404: no such container
275
+	:statuscode 500: server error
276
+
277
+
278
+Start a container
279
+*****************
280
+
281
+.. http:post:: /containers/(id)/start
282
+
283
+	Start the container ``id``
284
+
285
+	**Example request**:
286
+
287
+	.. sourcecode:: http
288
+
289
+	   POST /containers/e90e34656806/start HTTP/1.1
290
+	   
291
+	**Example response**:
292
+
293
+	.. sourcecode:: http
294
+
295
+	   HTTP/1.1 200 OK
296
+	   	
297
+	:statuscode 200: no error
298
+	:statuscode 404: no such container
299
+	:statuscode 500: server error
300
+
301
+
302
+Stop a contaier
303
+***************
304
+
305
+.. http:post:: /containers/(id)/stop
306
+
307
+	Stop the container ``id``
308
+
309
+	**Example request**:
310
+
311
+	.. sourcecode:: http
312
+
313
+	   POST /containers/e90e34656806/stop?t=5 HTTP/1.1
314
+	   
315
+	**Example response**:
316
+
317
+	.. sourcecode:: http
318
+
319
+	   HTTP/1.1 204 OK
320
+	   	
321
+	:query t: number of seconds to wait before killing the container
322
+	:statuscode 204: no error
323
+	:statuscode 404: no such container
324
+	:statuscode 500: server error
325
+
326
+
327
+Restart a container
328
+*******************
329
+
330
+.. http:post:: /containers/(id)/restart
331
+
332
+	Restart the container ``id``
333
+
334
+	**Example request**:
335
+
336
+	.. sourcecode:: http
337
+
338
+	   POST /containers/e90e34656806/restart?t=5 HTTP/1.1
339
+	   
340
+	**Example response**:
341
+
342
+	.. sourcecode:: http
343
+
344
+	   HTTP/1.1 204 OK
345
+	   	
346
+	:query t: number of seconds to wait before killing the container
347
+	:statuscode 204: no error
348
+	:statuscode 404: no such container
349
+	:statuscode 500: server error
350
+
351
+
352
+Kill a container
353
+****************
354
+
355
+.. http:post:: /containers/(id)/kill
356
+
357
+	Kill the container ``id``
358
+
359
+	**Example request**:
360
+
361
+	.. sourcecode:: http
362
+
363
+	   POST /containers/e90e34656806/kill HTTP/1.1
364
+	   
365
+	**Example response**:
366
+
367
+	.. sourcecode:: http
368
+
369
+	   HTTP/1.1 204 OK
370
+	   	
371
+	:statuscode 204: no error
372
+	:statuscode 404: no such container
373
+	:statuscode 500: server error
374
+
375
+
376
+Attach to a container
377
+*********************
378
+
379
+.. http:post:: /containers/(id)/attach
380
+
381
+	Attach to the container ``id``
382
+
383
+	**Example request**:
384
+
385
+	.. sourcecode:: http
386
+
387
+	   POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1
388
+	   
389
+	**Example response**:
390
+
391
+	.. sourcecode:: http
392
+
393
+	   HTTP/1.1 200 OK
394
+	   Content-Type: application/vnd.docker.raw-stream
395
+
396
+	   {{ STREAM }}
397
+	   	
398
+	:query logs: 1/True/true or 0/False/false, return logs. Default false
399
+	:query stream: 1/True/true or 0/False/false, return stream. Default false
400
+	:query stdin: 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false
401
+	:query stdout: 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false
402
+	:query stderr: 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false
403
+	:statuscode 200: no error
404
+	:statuscode 400: bad parameter
405
+	:statuscode 404: no such container
406
+	:statuscode 500: server error
407
+
408
+
409
+Wait a container
410
+****************
411
+
412
+.. http:post:: /containers/(id)/wait
413
+
414
+	Block until container ``id`` stops, then returns the exit code
415
+
416
+	**Example request**:
417
+
418
+	.. sourcecode:: http
419
+
420
+	   POST /containers/16253994b7c4/wait HTTP/1.1
421
+	   
422
+	**Example response**:
423
+
424
+	.. sourcecode:: http
425
+
426
+	   HTTP/1.1 200 OK
427
+	   Content-Type: application/json
428
+
429
+	   {"StatusCode":0}
430
+	   	
431
+	:statuscode 200: no error
432
+	:statuscode 404: no such container
433
+	:statuscode 500: server error
434
+
435
+
436
+Remove a container
437
+*******************
438
+
439
+.. http:delete:: /containers/(id)
440
+
441
+	Remove the container ``id`` from the filesystem
442
+
443
+	**Example request**:
444
+
445
+        .. sourcecode:: http
446
+
447
+           DELETE /containers/16253994b7c4?v=1 HTTP/1.1
448
+
449
+        **Example response**:
450
+
451
+        .. sourcecode:: http
452
+
453
+	   HTTP/1.1 204 OK
454
+
455
+	:query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false
456
+        :statuscode 204: no error
457
+	:statuscode 400: bad parameter
458
+        :statuscode 404: no such container
459
+        :statuscode 500: server error
460
+
461
+
462
+2.2 Images
463
+----------
464
+
465
+List Images
466
+***********
467
+
468
+.. http:get:: /images/(format)
469
+
470
+	List images ``format`` could be json or viz (json default)
471
+
472
+	**Example request**:
473
+
474
+	.. sourcecode:: http
475
+
476
+	   GET /images/json?all=0 HTTP/1.1
477
+
478
+	**Example response**:
479
+
480
+	.. sourcecode:: http
481
+
482
+	   HTTP/1.1 200 OK
483
+	   Content-Type: application/json
484
+	   
485
+	   [
486
+		{
487
+			"Repository":"base",
488
+			"Tag":"ubuntu-12.10",
489
+			"Id":"b750fe79269d",
490
+			"Created":1364102658
491
+		},
492
+		{
493
+			"Repository":"base",
494
+			"Tag":"ubuntu-quantal",
495
+			"Id":"b750fe79269d",
496
+			"Created":1364102658
497
+		}
498
+	   ]
499
+
500
+
501
+	**Example request**:
502
+
503
+	.. sourcecode:: http
504
+
505
+	   GET /images/viz HTTP/1.1
506
+
507
+	**Example response**:
508
+
509
+	.. sourcecode:: http
510
+
511
+	   HTTP/1.1 200 OK
512
+	   Content-Type: text/plain
513
+
514
+	   digraph docker {
515
+	   "d82cbacda43a" -> "074be284591f"
516
+	   "1496068ca813" -> "08306dc45919"
517
+	   "08306dc45919" -> "0e7893146ac2"
518
+	   "b750fe79269d" -> "1496068ca813"
519
+	   base -> "27cf78414709" [style=invis]
520
+	   "f71189fff3de" -> "9a33b36209ed"
521
+	   "27cf78414709" -> "b750fe79269d"
522
+	   "0e7893146ac2" -> "d6434d954665"
523
+	   "d6434d954665" -> "d82cbacda43a"
524
+	   base -> "e9aa60c60128" [style=invis]
525
+	   "074be284591f" -> "f71189fff3de"
526
+	   "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
527
+	   "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
528
+	   "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
529
+	   base [style=invisible]
530
+	   }
531
+ 
532
+	:query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default
533
+	:statuscode 200: no error
534
+	:statuscode 400: bad parameter
535
+	:statuscode 500: server error
536
+
537
+
538
+Create an image
539
+***************
540
+
541
+.. http:post:: /images/create
542
+
543
+	Create an image, either by pull it from the registry or by importing it
544
+
545
+	**Example request**:
546
+
547
+        .. sourcecode:: http
548
+
549
+           POST /images/create?fromImage=base HTTP/1.1
550
+
551
+        **Example response**:
552
+
553
+        .. sourcecode:: http
554
+
555
+           HTTP/1.1 200 OK
556
+	   Content-Type: application/json
557
+
558
+	   {"status":"Pulling..."}
559
+	   {"status":"Pulling", "progress":"1/? (n/a)"}
560
+	   {"error":"Invalid..."}
561
+	   ...
562
+
563
+        :query fromImage: name of the image to pull
564
+	:query fromSrc: source to import, - means stdin
565
+        :query repo: repository
566
+	:query tag: tag
567
+	:query registry: the registry to pull from
568
+        :statuscode 200: no error
569
+        :statuscode 500: server error
570
+
571
+
572
+Insert a file in a image
573
+************************
574
+
575
+.. http:post:: /images/(name)/insert
576
+
577
+	Insert a file from ``url`` in the image ``name`` at ``path``
578
+
579
+	**Example request**:
580
+
581
+        .. sourcecode:: http
582
+
583
+           POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
584
+
585
+	**Example response**:
586
+
587
+        .. sourcecode:: http
588
+
589
+           HTTP/1.1 200 OK
590
+	   Content-Type: application/json
591
+
592
+	   {"status":"Inserting..."}
593
+	   {"status":"Inserting", "progress":"1/? (n/a)"}
594
+	   {"error":"Invalid..."}
595
+	   ...
596
+
597
+	:statuscode 200: no error
598
+        :statuscode 500: server error
599
+
600
+
601
+Inspect an image
602
+****************
603
+
604
+.. http:get:: /images/(name)/json
605
+
606
+	Return low-level information on the image ``name``
607
+
608
+	**Example request**:
609
+
610
+	.. sourcecode:: http
611
+
612
+	   GET /images/base/json HTTP/1.1
613
+
614
+	**Example response**:
615
+
616
+        .. sourcecode:: http
617
+
618
+           HTTP/1.1 200 OK
619
+	   Content-Type: application/json
620
+
621
+	   {
622
+		"id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
623
+		"parent":"27cf784147099545",
624
+		"created":"2013-03-23T22:24:18.818426-07:00",
625
+		"container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
626
+		"container_config":
627
+			{
628
+				"Hostname":"",
629
+				"User":"",
630
+				"Memory":0,
631
+				"MemorySwap":0,
632
+				"AttachStdin":false,
633
+				"AttachStdout":false,
634
+				"AttachStderr":false,
635
+				"PortSpecs":null,
636
+				"Tty":true,
637
+				"OpenStdin":true,
638
+				"StdinOnce":false,
639
+				"Env":null,
640
+				"Cmd": ["/bin/bash"]
641
+				,"Dns":null,
642
+				"Image":"base",
643
+				"Volumes":null,
644
+				"VolumesFrom":""
645
+			}
646
+	   }
647
+
648
+	:statuscode 200: no error
649
+	:statuscode 404: no such image
650
+        :statuscode 500: server error
651
+
652
+
653
+Get the history of an image
654
+***************************
655
+
656
+.. http:get:: /images/(name)/history
657
+
658
+        Return the history of the image ``name``
659
+
660
+        **Example request**:
661
+
662
+        .. sourcecode:: http
663
+
664
+           GET /images/base/history HTTP/1.1
665
+
666
+        **Example response**:
667
+
668
+        .. sourcecode:: http
669
+
670
+           HTTP/1.1 200 OK
671
+	   Content-Type: application/json
672
+
673
+	   [
674
+		{
675
+			"Id":"b750fe79269d",
676
+			"Created":1364102658,
677
+			"CreatedBy":"/bin/bash"
678
+		},
679
+		{
680
+			"Id":"27cf78414709",
681
+			"Created":1364068391,
682
+			"CreatedBy":""
683
+		}
684
+	   ]
685
+
686
+        :statuscode 200: no error
687
+        :statuscode 404: no such image
688
+        :statuscode 500: server error
689
+
690
+
691
+Push an image on the registry
692
+*****************************
693
+
694
+.. http:post:: /images/(name)/push
695
+
696
+	Push the image ``name`` on the registry
697
+
698
+	 **Example request**:
699
+
700
+	 .. sourcecode:: http
701
+
702
+	    POST /images/test/push HTTP/1.1
703
+	    {{ authConfig }}
704
+
705
+	 **Example response**:
706
+
707
+        .. sourcecode:: http
708
+
709
+           HTTP/1.1 200 OK
710
+	   Content-Type: application/json
711
+
712
+	   {"status":"Pushing..."}
713
+	   {"status":"Pushing", "progress":"1/? (n/a)"}
714
+	   {"error":"Invalid..."}
715
+	   ...
716
+
717
+	:query registry: the registry you wan to push, optional
718
+	:statuscode 200: no error
719
+        :statuscode 404: no such image
720
+        :statuscode 500: server error
721
+
722
+
723
+Tag an image into a repository
724
+******************************
725
+
726
+.. http:post:: /images/(name)/tag
727
+
728
+	Tag the image ``name`` into a repository
729
+
730
+        **Example request**:
731
+
732
+        .. sourcecode:: http
733
+			
734
+	   POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1
735
+
736
+	**Example response**:
737
+
738
+        .. sourcecode:: http
739
+
740
+           HTTP/1.1 200 OK
741
+
742
+	:query repo: The repository to tag in
743
+	:query force: 1/True/true or 0/False/false, default false
744
+	:statuscode 200: no error
745
+	:statuscode 400: bad parameter
746
+	:statuscode 404: no such image
747
+	:statuscode 409: conflict
748
+        :statuscode 500: server error
749
+
750
+
751
+Remove an image
752
+***************
753
+
754
+.. http:delete:: /images/(name)
755
+
756
+	Remove the image ``name`` from the filesystem 
757
+	
758
+	**Example request**:
759
+
760
+	.. sourcecode:: http
761
+
762
+	   DELETE /images/test HTTP/1.1
763
+
764
+	**Example response**:
765
+
766
+        .. sourcecode:: http
767
+
768
+	   HTTP/1.1 200 OK
769
+	   Content-type: application/json
770
+
771
+	   [
772
+	    {"Untagged":"3e2f21a89f"},
773
+	    {"Deleted":"3e2f21a89f"},
774
+	    {"Deleted":"53b4f83ac9"}
775
+	   ]
776
+
777
+	:statuscode 204: no error
778
+        :statuscode 404: no such image
779
+	:statuscode 409: conflict
780
+        :statuscode 500: server error
781
+
782
+
783
+Search images
784
+*************
785
+
786
+.. http:get:: /images/search
787
+
788
+	Search for an image in the docker index
789
+	
790
+	**Example request**:
791
+
792
+        .. sourcecode:: http
793
+
794
+           GET /images/search?term=sshd HTTP/1.1
795
+
796
+	**Example response**:
797
+
798
+	.. sourcecode:: http
799
+
800
+	   HTTP/1.1 200 OK
801
+	   Content-Type: application/json
802
+	   
803
+	   [
804
+		{
805
+			"Name":"cespare/sshd",
806
+			"Description":""
807
+		},
808
+		{
809
+			"Name":"johnfuller/sshd",
810
+			"Description":""
811
+		},
812
+		{
813
+			"Name":"dhrp/mongodb-sshd",
814
+			"Description":""
815
+		}
816
+	   ]
817
+
818
+	   :query term: term to search
819
+	   :statuscode 200: no error
820
+	   :statuscode 500: server error
821
+
822
+
823
+2.3 Misc
824
+--------
825
+
826
+Build an image from Dockerfile via stdin
827
+****************************************
828
+
829
+.. http:post:: /build
830
+
831
+	Build an image from Dockerfile via stdin
832
+
833
+	**Example request**:
834
+
835
+        .. sourcecode:: http
836
+
837
+           POST /build HTTP/1.1
838
+	   
839
+	   {{ STREAM }}
840
+
841
+	**Example response**:
842
+
843
+        .. sourcecode:: http
844
+
845
+           HTTP/1.1 200 OK
846
+	   
847
+	   {{ STREAM }}
848
+
849
+	:query t: tag to be applied to the resulting image in case of success
850
+	:statuscode 200: no error
851
+        :statuscode 500: server error
852
+
853
+
854
+Check auth configuration
855
+************************
856
+
857
+.. http:post:: /auth
858
+
859
+        Get the default username and email
860
+
861
+        **Example request**:
862
+
863
+        .. sourcecode:: http
864
+
865
+           POST /auth HTTP/1.1
866
+	   Content-Type: application/json
867
+
868
+	   {
869
+		"username":"hannibal",
870
+		"password:"xxxx",
871
+		"email":"hannibal@a-team.com"
872
+	   }
873
+
874
+        **Example response**:
875
+
876
+        .. sourcecode:: http
877
+
878
+           HTTP/1.1 200 OK
879
+
880
+        :statuscode 200: no error
881
+        :statuscode 204: no error
882
+        :statuscode 500: server error
883
+
884
+
885
+Display system-wide information
886
+*******************************
887
+
888
+.. http:get:: /info
889
+
890
+	Display system-wide information
891
+	
892
+	**Example request**:
893
+
894
+        .. sourcecode:: http
895
+
896
+           GET /info HTTP/1.1
897
+
898
+        **Example response**:
899
+
900
+        .. sourcecode:: http
901
+
902
+           HTTP/1.1 200 OK
903
+	   Content-Type: application/json
904
+
905
+	   {
906
+		"Containers":11,
907
+		"Images":16,
908
+		"Debug":false,
909
+		"NFd": 11,
910
+		"NGoroutines":21,
911
+		"MemoryLimit":true,
912
+		"SwapLimit":false
913
+	   }
914
+
915
+        :statuscode 200: no error
916
+        :statuscode 500: server error
917
+
918
+
919
+Show the docker version information
920
+***********************************
921
+
922
+.. http:get:: /version
923
+
924
+	Show the docker version information
925
+
926
+	**Example request**:
927
+
928
+        .. sourcecode:: http
929
+
930
+           GET /version HTTP/1.1
931
+
932
+        **Example response**:
933
+
934
+        .. sourcecode:: http
935
+
936
+           HTTP/1.1 200 OK
937
+	   Content-Type: application/json
938
+
939
+	   {
940
+		"Version":"0.2.2",
941
+		"GitCommit":"5a2a5cc+CHANGES",
942
+		"GoVersion":"go1.0.3"
943
+	   }
944
+
945
+        :statuscode 200: no error
946
+	:statuscode 500: server error
947
+
948
+
949
+Create a new image from a container's changes
950
+*********************************************
951
+
952
+.. http:post:: /commit
953
+
954
+	Create a new image from a container's changes
955
+
956
+	**Example request**:
957
+
958
+        .. sourcecode:: http
959
+
960
+           POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
961
+
962
+        **Example response**:
963
+
964
+        .. sourcecode:: http
965
+
966
+           HTTP/1.1 201 OK
967
+	   Content-Type: application/vnd.docker.raw-stream
968
+
969
+           {"Id":"596069db4bf5"}
970
+
971
+	:query container: source container
972
+	:query repo: repository
973
+	:query tag: tag
974
+	:query m: commit message
975
+	:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
976
+	:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
977
+        :statuscode 201: no error
978
+	:statuscode 404: no such container
979
+        :statuscode 500: server error
980
+
981
+
982
+3. Going further
983
+================
984
+
985
+3.1 Inside 'docker run'
986
+-----------------------
987
+
988
+Here are the steps of 'docker run' :
989
+
990
+* Create the container
991
+* If the status code is 404, it means the image doesn't exists:
992
+        * Try to pull it
993
+        * Then retry to create the container
994
+* Start the container
995
+* If you are not in detached mode:
996
+        * Attach to the container, using logs=1 (to have stdout and stderr from the container's start) and stream=1
997
+* If in detached mode or only stdin is attached:
998
+	* Display the container's id
999
+
1000
+
1001
+3.2 Hijacking
1002
+-------------
1003
+
1004
+In this version of the API, /attach, uses hijacking to transport stdin, stdout and stderr on the same socket. This might change in the future.
1005
+
1006
+3.3 CORS Requests
1007
+-----------------
1008
+
1009
+To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode.
1010
+    
1011
+    docker -d -H="192.168.1.9:4243" -api-enable-cors
1012
+
... ...
@@ -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
... ...
@@ -35,13 +35,16 @@ Most frequently asked questions.
35 35
 
36 36
     You can find more answers on:
37 37
 
38
-    * `IRC: docker on freenode`_
38
+    * `Docker club mailinglist`_
39
+    * `IRC, docker on freenode`_
39 40
     * `Github`_
40 41
     * `Ask questions on Stackoverflow`_
41 42
     * `Join the conversation on Twitter`_
42 43
 
44
+
45
+    .. _Docker club mailinglist: https://groups.google.com/d/forum/docker-club
43 46
     .. _the repo: http://www.github.com/dotcloud/docker
44
-    .. _IRC: docker on freenode: docker on freenode: irc://chat.freenode.net#docker
47
+    .. _IRC, docker on freenode: irc://chat.freenode.net#docker
45 48
     .. _Github: http://www.github.com/dotcloud/docker
46 49
     .. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker
47 50
     .. _Join the conversation on Twitter: http://twitter.com/getdocker
... ...
@@ -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,8 +51,16 @@
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>
... ...
@@ -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>
324 327
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.
... ...
@@ -13,7 +13,7 @@ run	apt-get update
13 13
 # Packages required to checkout, build and upload docker
14 14
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd
15 15
 run	DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
16
-run	curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.linux-amd64.tar.gz
16
+run	curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.1.linux-amd64.tar.gz
17 17
 run	tar -C /usr/local -xzf /go.tar.gz
18 18
 run	echo "export PATH=/usr/local/go/bin:$PATH" > /.bashrc
19 19
 run	echo "export PATH=/usr/local/go/bin:$PATH" > /.bash_profile
20 20
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Daniel Mizyrycki <daniel@dotcloud.com>
... ...
@@ -140,6 +140,8 @@ func MountAUFS(ro []string, rw string, target string) error {
140 140
 	}
141 141
 	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
142 142
 
143
+	branches += ",xino=/dev/shm/aufs.xino"
144
+
143 145
 	//if error, try to load aufs kernel module
144 146
 	if err := mount("none", target, "aufs", 0, branches); err != nil {
145 147
 		log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
... ...
@@ -485,20 +485,38 @@ type Nat struct {
485 485
 
486 486
 func parseNat(spec string) (*Nat, error) {
487 487
 	var nat Nat
488
-	// If spec starts with ':', external and internal ports must be the same.
489
-	// This might fail if the requested external port is not available.
490
-	var sameFrontend bool
491
-	if spec[0] == ':' {
492
-		sameFrontend = true
493
-		spec = spec[1:]
494
-	}
495
-	port, err := strconv.ParseUint(spec, 10, 16)
496
-	if err != nil {
497
-		return nil, err
498
-	}
499
-	nat.Backend = int(port)
500
-	if sameFrontend {
501
-		nat.Frontend = nat.Backend
488
+
489
+	if strings.Contains(spec, ":") {
490
+		specParts := strings.Split(spec, ":")
491
+		if len(specParts) != 2 {
492
+			return nil, fmt.Errorf("Invalid port format.")
493
+		}
494
+		// If spec starts with ':', external and internal ports must be the same.
495
+		// This might fail if the requested external port is not available.
496
+		var sameFrontend bool
497
+		if len(specParts[0]) == 0 {
498
+			sameFrontend = true
499
+		} else {
500
+			front, err := strconv.ParseUint(specParts[0], 10, 16)
501
+			if err != nil {
502
+				return nil, err
503
+			}
504
+			nat.Frontend = int(front)
505
+		}
506
+		back, err := strconv.ParseUint(specParts[1], 10, 16)
507
+		if err != nil {
508
+			return nil, err
509
+		}
510
+		nat.Backend = int(back)
511
+		if sameFrontend {
512
+			nat.Frontend = nat.Backend
513
+		}
514
+	} else {
515
+		port, err := strconv.ParseUint(spec, 10, 16)
516
+		if err != nil {
517
+			return nil, err
518
+		}
519
+		nat.Backend = int(port)
502 520
 	}
503 521
 	nat.Proto = "tcp"
504 522
 	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 {
... ...
@@ -473,10 +473,7 @@ type Registry struct {
473 473
 	authConfig *auth.AuthConfig
474 474
 }
475 475
 
476
-func NewRegistry(root string) *Registry {
477
-	// If the auth file does not exist, keep going
478
-	authConfig, _ := auth.LoadConfig(root)
479
-
476
+func NewRegistry(root string, authConfig *auth.AuthConfig) *Registry {
480 477
 	httpTransport := &http.Transport{
481 478
 		DisableKeepAlives: true,
482 479
 		Proxy: http.ProxyFromEnvironment,
... ...
@@ -17,7 +17,7 @@ import (
17 17
 )
18 18
 
19 19
 const unitTestImageName string = "docker-ut"
20
-
20
+const unitTestImageId string = "e9aa60c60128cad1"
21 21
 const unitTestStoreBase string = "/var/lib/docker/unit-tests"
22 22
 
23 23
 func nuke(runtime *Runtime) error {
... ...
@@ -68,7 +68,7 @@ func init() {
68 68
 		runtime: runtime,
69 69
 	}
70 70
 	// Retrieve the Image
71
-	if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false)); err != nil {
71
+	if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false), nil); err != nil {
72 72
 		panic(err)
73 73
 	}
74 74
 }
... ...
@@ -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"
... ...
@@ -54,7 +55,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
54 54
 
55 55
 func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
56 56
 
57
-	results, err := registry.NewRegistry(srv.runtime.root).SearchRepositories(term)
57
+	results, err := registry.NewRegistry(srv.runtime.root, nil).SearchRepositories(term)
58 58
 	if err != nil {
59 59
 		return nil, err
60 60
 	}
... ...
@@ -63,9 +64,6 @@ func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
63 63
 	for _, repo := range results.Results {
64 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
 	}
... ...
@@ -336,8 +334,8 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
336 336
 	return nil
337 337
 }
338 338
 
339
-func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, sf *utils.StreamFormatter) error {
340
-	out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress()))
339
+func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, local, remote, askedTag string, sf *utils.StreamFormatter) error {
340
+	out.Write(sf.FormatStatus("Pulling repository %s from %s", local, auth.IndexServerAddress()))
341 341
 	repoData, err := r.GetRepositoryData(remote)
342 342
 	if err != nil {
343 343
 		return err
... ...
@@ -364,7 +362,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
364 364
 		// Otherwise, check that the tag exists and use only that one
365 365
 		id, exists := tagsList[askedTag]
366 366
 		if !exists {
367
-			return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, remote)
367
+			return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, local)
368 368
 		}
369 369
 		repoData.ImgList[id].Tag = askedTag
370 370
 	}
... ...
@@ -392,7 +390,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
392 392
 		if askedTag != "" && tag != askedTag {
393 393
 			continue
394 394
 		}
395
-		if err := srv.runtime.repositories.Set(remote, tag, id, true); err != nil {
395
+		if err := srv.runtime.repositories.Set(local, tag, id, true); err != nil {
396 396
 			return err
397 397
 		}
398 398
 	}
... ...
@@ -403,8 +401,8 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
403 403
 	return nil
404 404
 }
405 405
 
406
-func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
407
-	r := registry.NewRegistry(srv.runtime.root)
406
+func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error {
407
+	r := registry.NewRegistry(srv.runtime.root, authConfig)
408 408
 	out = utils.NewWriteFlusher(out)
409 409
 	if endpoint != "" {
410 410
 		if err := srv.pullImage(r, out, name, endpoint, nil, sf); err != nil {
... ...
@@ -412,8 +410,12 @@ func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *util
412 412
 		}
413 413
 		return nil
414 414
 	}
415
-
416
-	if err := srv.pullRepository(r, out, name, tag, sf); err != nil {
415
+	remote := name
416
+	parts := strings.Split(name, "/")
417
+	if len(parts) > 2 {
418
+		remote = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/")))
419
+	}
420
+	if err := srv.pullRepository(r, out, name, remote, tag, sf); err != nil {
417 421
 		return err
418 422
 	}
419 423
 
... ...
@@ -495,7 +497,13 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
495 495
 	}
496 496
 	out.Write(sf.FormatStatus("Sending image list"))
497 497
 
498
-	repoData, err := r.PushImageJSONIndex(name, imgList, false)
498
+	srvName := name
499
+	parts := strings.Split(name, "/")
500
+	if len(parts) > 2 {
501
+		srvName = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/")))
502
+	}
503
+
504
+	repoData, err := r.PushImageJSONIndex(srvName, imgList, false)
499 505
 	if err != nil {
500 506
 		return err
501 507
 	}
... ...
@@ -512,14 +520,14 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
512 512
 				// FIXME: Continue on error?
513 513
 				return err
514 514
 			}
515
-			out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+name+"/"+elem.Tag))
516
-			if err := r.PushRegistryTag(name, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
515
+			out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+srvName+"/"+elem.Tag))
516
+			if err := r.PushRegistryTag(srvName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
517 517
 				return err
518 518
 			}
519 519
 		}
520 520
 	}
521 521
 
522
-	if _, err := r.PushImageJSONIndex(name, imgList, true); err != nil {
522
+	if _, err := r.PushImageJSONIndex(srvName, imgList, true); err != nil {
523 523
 		return err
524 524
 	}
525 525
 	return nil
... ...
@@ -585,10 +593,10 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
585 585
 	return nil
586 586
 }
587 587
 
588
-func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
588
+func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error {
589 589
 	out = utils.NewWriteFlusher(out)
590 590
 	img, err := srv.runtime.graph.Get(name)
591
-	r := registry.NewRegistry(srv.runtime.root)
591
+	r := registry.NewRegistry(srv.runtime.root, authConfig)
592 592
 
593 593
 	if err != nil {
594 594
 		out.Write(sf.FormatStatus("The push refers to a repository [%s] (len: %d)", name, len(srv.runtime.repositories.Repositories[name])))
... ...
@@ -716,17 +724,112 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
716 716
 	return nil
717 717
 }
718 718
 
719
-func (srv *Server) ImageDelete(name string) error {
720
-	img, err := srv.runtime.repositories.LookupImage(name)
719
+var ErrImageReferenced = errors.New("Image referenced by a repository")
720
+
721
+func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error {
722
+	// If the image is referenced by a repo, do not delete
723
+	if len(srv.runtime.repositories.ByID()[id]) != 0 {
724
+		return ErrImageReferenced
725
+	}
726
+
727
+	// If the image is not referenced but has children, go recursive
728
+	referenced := false
729
+	byParents, err := srv.runtime.graph.ByParent()
721 730
 	if err != nil {
722
-		return fmt.Errorf("No such image: %s", name)
731
+		return err
732
+	}
733
+	for _, img := range byParents[id] {
734
+		if err := srv.deleteImageAndChildren(img.ID, imgs); err != nil {
735
+			if err != ErrImageReferenced {
736
+				return err
737
+			}
738
+			referenced = true
739
+		}
723 740
 	}
724
-	if err := srv.runtime.graph.Delete(img.ID); err != nil {
725
-		return fmt.Errorf("Error deleting image %s: %s", name, err.Error())
741
+	if referenced {
742
+		return ErrImageReferenced
743
+	}
744
+
745
+	// If the image is not referenced and has no children, remove it
746
+	byParents, err = srv.runtime.graph.ByParent()
747
+	if err != nil {
748
+		return err
749
+	}
750
+	if len(byParents[id]) == 0 {
751
+		if err := srv.runtime.repositories.DeleteAll(id); err != nil {
752
+			return err
753
+		}
754
+		err := srv.runtime.graph.Delete(id)
755
+		if err != nil {
756
+			return err
757
+		}
758
+		*imgs = append(*imgs, APIRmi{Deleted: utils.TruncateID(id)})
759
+		return nil
760
+	}
761
+	return nil
762
+}
763
+
764
+func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error {
765
+	if img.Parent != "" {
766
+		parent, err := srv.runtime.graph.Get(img.Parent)
767
+		if err != nil {
768
+			return err
769
+		}
770
+		// Remove all children images
771
+		if err := srv.deleteImageAndChildren(img.Parent, imgs); err != nil {
772
+			return err
773
+		}
774
+		return srv.deleteImageParents(parent, imgs)
726 775
 	}
727 776
 	return nil
728 777
 }
729 778
 
779
+func (srv *Server) deleteImage(img *Image, repoName, tag string) (*[]APIRmi, error) {
780
+	//Untag the current image
781
+	var imgs []APIRmi
782
+	tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag)
783
+	if err != nil {
784
+		return nil, err
785
+	}
786
+	if tagDeleted {
787
+		imgs = append(imgs, APIRmi{Untagged: img.ShortID()})
788
+	}
789
+	if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
790
+		if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil {
791
+			if err != ErrImageReferenced {
792
+				return &imgs, err
793
+			}
794
+		} else if err := srv.deleteImageParents(img, &imgs); err != nil {
795
+			if err != ErrImageReferenced {
796
+				return &imgs, err
797
+			}
798
+		}
799
+	}
800
+	return &imgs, nil
801
+}
802
+
803
+func (srv *Server) ImageDelete(name string, autoPrune bool) (*[]APIRmi, error) {
804
+	img, err := srv.runtime.repositories.LookupImage(name)
805
+	if err != nil {
806
+		return nil, fmt.Errorf("No such image: %s", name)
807
+	}
808
+	if !autoPrune {
809
+		if err := srv.runtime.graph.Delete(img.ID); err != nil {
810
+			return nil, fmt.Errorf("Error deleting image %s: %s", name, err.Error())
811
+		}
812
+		return nil, nil
813
+	}
814
+
815
+	var tag string
816
+	if strings.Contains(name, ":") {
817
+		nameParts := strings.Split(name, ":")
818
+		name = nameParts[0]
819
+		tag = nameParts[1]
820
+	}
821
+
822
+	return srv.deleteImage(img, name, tag)
823
+}
824
+
730 825
 func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error) {
731 826
 
732 827
 	// Retrieve all images
... ...
@@ -875,7 +978,7 @@ func (srv *Server) ImageInspect(name string) (*Image, error) {
875 875
 	return nil, fmt.Errorf("No such image: %s", name)
876 876
 }
877 877
 
878
-func NewServer(autoRestart bool) (*Server, error) {
878
+func NewServer(autoRestart, enableCors bool) (*Server, error) {
879 879
 	if runtime.GOARCH != "amd64" {
880 880
 		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
881 881
 	}
... ...
@@ -884,12 +987,14 @@ func NewServer(autoRestart bool) (*Server, error) {
884 884
 		return nil, err
885 885
 	}
886 886
 	srv := &Server{
887
-		runtime: runtime,
887
+		runtime:    runtime,
888
+		enableCors: enableCors,
888 889
 	}
889 890
 	runtime.srv = srv
890 891
 	return srv, nil
891 892
 }
892 893
 
893 894
 type Server struct {
894
-	runtime *Runtime
895
+	runtime    *Runtime
896
+	enableCors bool
895 897
 }
... ...
@@ -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 {
... ...
@@ -110,6 +110,52 @@ func (store *TagStore) ImageName(id string) string {
110 110
 	return utils.TruncateID(id)
111 111
 }
112 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()
157
+}
158
+
113 159
 func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
114 160
 	img, err := store.LookupImage(imageName)
115 161
 	if err != nil {
... ...
@@ -133,7 +179,7 @@ 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
 	}
... ...
@@ -151,14 +197,20 @@ func (store *TagStore) Get(repoName string) (Repository, error) {
151 151
 	return nil, nil
152 152
 }
153 153
 
154
-func (store *TagStore) GetImage(repoName, tag string) (*Image, error) {
154
+func (store *TagStore) GetImage(repoName, tagOrId string) (*Image, error) {
155 155
 	repo, err := store.Get(repoName)
156 156
 	if err != nil {
157 157
 		return nil, err
158 158
 	} else if repo == nil {
159 159
 		return nil, nil
160 160
 	}
161
-	if revision, exists := repo[tag]; exists {
161
+	//go through all the tags, to see if tag is in fact an ID
162
+	for _, revision := range repo {
163
+		if strings.HasPrefix(revision, tagOrId) {
164
+			return store.graph.Get(revision)
165
+		}
166
+	}
167
+	if revision, exists := repo[tagOrId]; exists {
162 168
 		return store.graph.Get(revision)
163 169
 	}
164 170
 	return nil, nil
165 171
new file mode 100644
... ...
@@ -0,0 +1,49 @@
0
+package docker
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestLookupImage(t *testing.T) {
7
+	runtime, err := newTestRuntime()
8
+	if err != nil {
9
+		t.Fatal(err)
10
+	}
11
+	defer nuke(runtime)
12
+
13
+	if img, err := runtime.repositories.LookupImage(unitTestImageName); err != nil {
14
+		t.Fatal(err)
15
+	} else if img == nil {
16
+		t.Errorf("Expected 1 image, none found")
17
+	}
18
+
19
+	if img, err := runtime.repositories.LookupImage(unitTestImageName + ":" + DEFAULTTAG); err != nil {
20
+		t.Fatal(err)
21
+	} else if img == nil {
22
+		t.Errorf("Expected 1 image, none found")
23
+	}
24
+
25
+	if img, err := runtime.repositories.LookupImage(unitTestImageName + ":" + "fail"); err == nil {
26
+		t.Errorf("Expected error, none found")
27
+	} else if img != nil {
28
+		t.Errorf("Expected 0 image, 1 found")
29
+	}
30
+
31
+	if img, err := runtime.repositories.LookupImage("fail:fail"); err == nil {
32
+		t.Errorf("Expected error, none found")
33
+	} else if img != nil {
34
+		t.Errorf("Expected 0 image, 1 found")
35
+	}
36
+
37
+	if img, err := runtime.repositories.LookupImage(unitTestImageId); err != nil {
38
+		t.Fatal(err)
39
+	} else if img == nil {
40
+		t.Errorf("Expected 1 image, none found")
41
+	}
42
+
43
+	if img, err := runtime.repositories.LookupImage(unitTestImageName + ":" + unitTestImageId); err != nil {
44
+		t.Fatal(err)
45
+	} else if img == nil {
46
+		t.Errorf("Expected 1 image, none found")
47
+	}
48
+}