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