| ... | ... |
@@ -15,6 +15,7 @@ Brian McCallister <brianm@skife.org> |
| 15 | 15 |
Bruno Bigras <bigras.bruno@gmail.com> |
| 16 | 16 |
Caleb Spare <cespare@gmail.com> |
| 17 | 17 |
Charles Hooper <charles.hooper@dotcloud.com> |
| 18 |
+Daniel Gasienica <daniel@gasienica.ch> |
|
| 18 | 19 |
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> |
| 19 | 20 |
Daniel Robinson <gottagetmac@gmail.com> |
| 20 | 21 |
Daniel Von Fange <daniel@leancoder.com> |
| ... | ... |
@@ -1,5 +1,19 @@ |
| 1 | 1 |
# Changelog |
| 2 | 2 |
|
| 3 |
+## 0.4.0 (2013-06-03) |
|
| 4 |
+ + Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile |
|
| 5 |
+ + Introducing Remote API: control Docker programmatically using a simple HTTP/json API |
|
| 6 |
+ * Runtime: various reliability and usability improvements |
|
| 7 |
+ |
|
| 8 |
+## 0.3.4 (2013-05-30) |
|
| 9 |
+ + Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile |
|
| 10 |
+ + Builder: 'docker build -t FOO' applies the tag FOO to the newly built container. |
|
| 11 |
+ + Runtime: interactive TTYs correctly handle window resize |
|
| 12 |
+ * Runtime: fix how configuration is merged between layers |
|
| 13 |
+ + Remote API: split stdout and stderr on 'docker run' |
|
| 14 |
+ + Remote API: optionally listen on a different IP and port (use at your own risk) |
|
| 15 |
+ * Documentation: improved install instructions. |
|
| 16 |
+ |
|
| 3 | 17 |
## 0.3.3 (2013-05-23) |
| 4 | 18 |
- Registry: Fix push regression |
| 5 | 19 |
- Various bugfixes |
| ... | ... |
@@ -13,7 +13,7 @@ import ( |
| 13 | 13 |
"strings" |
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 |
-const API_VERSION = 1.1 |
|
| 16 |
+const APIVERSION = 1.1 |
|
| 17 | 17 |
|
| 18 | 18 |
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
| 19 | 19 |
conn, _, err := w.(http.Hijacker).Hijack() |
| ... | ... |
@@ -45,12 +45,14 @@ func httpError(w http.ResponseWriter, err error) {
|
| 45 | 45 |
http.Error(w, err.Error(), http.StatusNotFound) |
| 46 | 46 |
} else if strings.HasPrefix(err.Error(), "Bad parameter") {
|
| 47 | 47 |
http.Error(w, err.Error(), http.StatusBadRequest) |
| 48 |
+ } else if strings.HasPrefix(err.Error(), "Impossible") {
|
|
| 49 |
+ http.Error(w, err.Error(), http.StatusNotAcceptable) |
|
| 48 | 50 |
} else {
|
| 49 | 51 |
http.Error(w, err.Error(), http.StatusInternalServerError) |
| 50 | 52 |
} |
| 51 | 53 |
} |
| 52 | 54 |
|
| 53 |
-func writeJson(w http.ResponseWriter, b []byte) {
|
|
| 55 |
+func writeJSON(w http.ResponseWriter, b []byte) {
|
|
| 54 | 56 |
w.Header().Set("Content-Type", "application/json")
|
| 55 | 57 |
w.Write(b) |
| 56 | 58 |
} |
| ... | ... |
@@ -80,7 +82,7 @@ func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reques |
| 80 | 80 |
if err != nil {
|
| 81 | 81 |
return err |
| 82 | 82 |
} |
| 83 |
- writeJson(w, b) |
|
| 83 |
+ writeJSON(w, b) |
|
| 84 | 84 |
return nil |
| 85 | 85 |
} |
| 86 | 86 |
|
| ... | ... |
@@ -109,11 +111,11 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque |
| 109 | 109 |
} |
| 110 | 110 |
|
| 111 | 111 |
if status != "" {
|
| 112 |
- b, err := json.Marshal(&ApiAuth{Status: status})
|
|
| 112 |
+ b, err := json.Marshal(&APIAuth{Status: status})
|
|
| 113 | 113 |
if err != nil {
|
| 114 | 114 |
return err |
| 115 | 115 |
} |
| 116 |
- writeJson(w, b) |
|
| 116 |
+ writeJSON(w, b) |
|
| 117 | 117 |
return nil |
| 118 | 118 |
} |
| 119 | 119 |
w.WriteHeader(http.StatusNoContent) |
| ... | ... |
@@ -126,7 +128,7 @@ func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Req |
| 126 | 126 |
if err != nil {
|
| 127 | 127 |
return err |
| 128 | 128 |
} |
| 129 |
- writeJson(w, b) |
|
| 129 |
+ writeJSON(w, b) |
|
| 130 | 130 |
return nil |
| 131 | 131 |
} |
| 132 | 132 |
|
| ... | ... |
@@ -155,7 +157,7 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r |
| 155 | 155 |
return nil |
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 |
-func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 158 |
+func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 159 | 159 |
if err := parseForm(r); err != nil {
|
| 160 | 160 |
return err |
| 161 | 161 |
} |
| ... | ... |
@@ -174,7 +176,7 @@ func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http. |
| 174 | 174 |
if err != nil {
|
| 175 | 175 |
return err |
| 176 | 176 |
} |
| 177 |
- writeJson(w, b) |
|
| 177 |
+ writeJSON(w, b) |
|
| 178 | 178 |
return nil |
| 179 | 179 |
} |
| 180 | 180 |
|
| ... | ... |
@@ -191,7 +193,7 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques |
| 191 | 191 |
if err != nil {
|
| 192 | 192 |
return err |
| 193 | 193 |
} |
| 194 |
- writeJson(w, b) |
|
| 194 |
+ writeJSON(w, b) |
|
| 195 | 195 |
return nil |
| 196 | 196 |
} |
| 197 | 197 |
|
| ... | ... |
@@ -208,7 +210,7 @@ func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *ht |
| 208 | 208 |
if err != nil {
|
| 209 | 209 |
return err |
| 210 | 210 |
} |
| 211 |
- writeJson(w, b) |
|
| 211 |
+ writeJSON(w, b) |
|
| 212 | 212 |
return nil |
| 213 | 213 |
} |
| 214 | 214 |
|
| ... | ... |
@@ -225,11 +227,11 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r |
| 225 | 225 |
if err != nil {
|
| 226 | 226 |
return err |
| 227 | 227 |
} |
| 228 |
- writeJson(w, b) |
|
| 228 |
+ writeJSON(w, b) |
|
| 229 | 229 |
return nil |
| 230 | 230 |
} |
| 231 | 231 |
|
| 232 |
-func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 232 |
+func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 233 | 233 |
if err := parseForm(r); err != nil {
|
| 234 | 234 |
return err |
| 235 | 235 |
} |
| ... | ... |
@@ -249,7 +251,7 @@ func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *h |
| 249 | 249 |
if err != nil {
|
| 250 | 250 |
return err |
| 251 | 251 |
} |
| 252 |
- writeJson(w, b) |
|
| 252 |
+ writeJSON(w, b) |
|
| 253 | 253 |
return nil |
| 254 | 254 |
} |
| 255 | 255 |
|
| ... | ... |
@@ -292,12 +294,12 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req |
| 292 | 292 |
if err != nil {
|
| 293 | 293 |
return err |
| 294 | 294 |
} |
| 295 |
- b, err := json.Marshal(&ApiId{id})
|
|
| 295 |
+ b, err := json.Marshal(&APIID{id})
|
|
| 296 | 296 |
if err != nil {
|
| 297 | 297 |
return err |
| 298 | 298 |
} |
| 299 | 299 |
w.WriteHeader(http.StatusCreated) |
| 300 |
- writeJson(w, b) |
|
| 300 |
+ writeJSON(w, b) |
|
| 301 | 301 |
return nil |
| 302 | 302 |
} |
| 303 | 303 |
|
| ... | ... |
@@ -312,16 +314,25 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht |
| 312 | 312 |
tag := r.Form.Get("tag")
|
| 313 | 313 |
repo := r.Form.Get("repo")
|
| 314 | 314 |
|
| 315 |
+ if version > 1.0 {
|
|
| 316 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 317 |
+ } |
|
| 318 |
+ sf := utils.NewStreamFormatter(version > 1.0) |
|
| 315 | 319 |
if image != "" { //pull
|
| 316 | 320 |
registry := r.Form.Get("registry")
|
| 317 |
- if version > 1.0 {
|
|
| 318 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 319 |
- } |
|
| 320 |
- if err := srv.ImagePull(image, tag, registry, w, version > 1.0); err != nil {
|
|
| 321 |
+ if err := srv.ImagePull(image, tag, registry, w, sf); err != nil {
|
|
| 322 |
+ if sf.Used() {
|
|
| 323 |
+ w.Write(sf.FormatError(err)) |
|
| 324 |
+ return nil |
|
| 325 |
+ } |
|
| 321 | 326 |
return err |
| 322 | 327 |
} |
| 323 | 328 |
} else { //import
|
| 324 |
- if err := srv.ImageImport(src, repo, tag, r.Body, w); err != nil {
|
|
| 329 |
+ if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil {
|
|
| 330 |
+ if sf.Used() {
|
|
| 331 |
+ w.Write(sf.FormatError(err)) |
|
| 332 |
+ return nil |
|
| 333 |
+ } |
|
| 325 | 334 |
return err |
| 326 | 335 |
} |
| 327 | 336 |
} |
| ... | ... |
@@ -342,7 +353,7 @@ func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *htt |
| 342 | 342 |
if err != nil {
|
| 343 | 343 |
return err |
| 344 | 344 |
} |
| 345 |
- writeJson(w, b) |
|
| 345 |
+ writeJSON(w, b) |
|
| 346 | 346 |
return nil |
| 347 | 347 |
} |
| 348 | 348 |
|
| ... | ... |
@@ -357,16 +368,22 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht |
| 357 | 357 |
return fmt.Errorf("Missing parameter")
|
| 358 | 358 |
} |
| 359 | 359 |
name := vars["name"] |
| 360 |
- |
|
| 361 |
- imgId, err := srv.ImageInsert(name, url, path, w) |
|
| 360 |
+ if version > 1.0 {
|
|
| 361 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 362 |
+ } |
|
| 363 |
+ sf := utils.NewStreamFormatter(version > 1.0) |
|
| 364 |
+ imgID, err := srv.ImageInsert(name, url, path, w, sf) |
|
| 362 | 365 |
if err != nil {
|
| 363 |
- return err |
|
| 366 |
+ if sf.Used() {
|
|
| 367 |
+ w.Write(sf.FormatError(err)) |
|
| 368 |
+ return nil |
|
| 369 |
+ } |
|
| 364 | 370 |
} |
| 365 |
- b, err := json.Marshal(&ApiId{Id: imgId})
|
|
| 371 |
+ b, err := json.Marshal(&APIID{ID: imgID})
|
|
| 366 | 372 |
if err != nil {
|
| 367 | 373 |
return err |
| 368 | 374 |
} |
| 369 |
- writeJson(w, b) |
|
| 375 |
+ writeJSON(w, b) |
|
| 370 | 376 |
return nil |
| 371 | 377 |
} |
| 372 | 378 |
|
| ... | ... |
@@ -380,8 +397,15 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http |
| 380 | 380 |
return fmt.Errorf("Missing parameter")
|
| 381 | 381 |
} |
| 382 | 382 |
name := vars["name"] |
| 383 |
- |
|
| 384 |
- if err := srv.ImagePush(name, registry, w); err != nil {
|
|
| 383 |
+ if version > 1.0 {
|
|
| 384 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 385 |
+ } |
|
| 386 |
+ sf := utils.NewStreamFormatter(version > 1.0) |
|
| 387 |
+ if err := srv.ImagePush(name, registry, w, sf); err != nil {
|
|
| 388 |
+ if sf.Used() {
|
|
| 389 |
+ w.Write(sf.FormatError(err)) |
|
| 390 |
+ return nil |
|
| 391 |
+ } |
|
| 385 | 392 |
return err |
| 386 | 393 |
} |
| 387 | 394 |
return nil |
| ... | ... |
@@ -397,8 +421,8 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r |
| 397 | 397 |
return err |
| 398 | 398 |
} |
| 399 | 399 |
|
| 400 |
- out := &ApiRun{
|
|
| 401 |
- Id: id, |
|
| 400 |
+ out := &APIRun{
|
|
| 401 |
+ ID: id, |
|
| 402 | 402 |
} |
| 403 | 403 |
if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
|
| 404 | 404 |
log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
|
| ... | ... |
@@ -413,7 +437,7 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r |
| 413 | 413 |
return err |
| 414 | 414 |
} |
| 415 | 415 |
w.WriteHeader(http.StatusCreated) |
| 416 |
- writeJson(w, b) |
|
| 416 |
+ writeJSON(w, b) |
|
| 417 | 417 |
return nil |
| 418 | 418 |
} |
| 419 | 419 |
|
| ... | ... |
@@ -510,11 +534,11 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r * |
| 510 | 510 |
if err != nil {
|
| 511 | 511 |
return err |
| 512 | 512 |
} |
| 513 |
- b, err := json.Marshal(&ApiWait{StatusCode: status})
|
|
| 513 |
+ b, err := json.Marshal(&APIWait{StatusCode: status})
|
|
| 514 | 514 |
if err != nil {
|
| 515 | 515 |
return err |
| 516 | 516 |
} |
| 517 |
- writeJson(w, b) |
|
| 517 |
+ writeJSON(w, b) |
|
| 518 | 518 |
return nil |
| 519 | 519 |
} |
| 520 | 520 |
|
| ... | ... |
@@ -601,7 +625,7 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r |
| 601 | 601 |
if err != nil {
|
| 602 | 602 |
return err |
| 603 | 603 |
} |
| 604 |
- writeJson(w, b) |
|
| 604 |
+ writeJSON(w, b) |
|
| 605 | 605 |
return nil |
| 606 | 606 |
} |
| 607 | 607 |
|
| ... | ... |
@@ -619,17 +643,17 @@ func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *htt |
| 619 | 619 |
if err != nil {
|
| 620 | 620 |
return err |
| 621 | 621 |
} |
| 622 |
- writeJson(w, b) |
|
| 622 |
+ writeJSON(w, b) |
|
| 623 | 623 |
return nil |
| 624 | 624 |
} |
| 625 | 625 |
|
| 626 | 626 |
func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 627 |
- apiConfig := &ApiImageConfig{}
|
|
| 627 |
+ apiConfig := &APIImageConfig{}
|
|
| 628 | 628 |
if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
|
| 629 | 629 |
return err |
| 630 | 630 |
} |
| 631 | 631 |
|
| 632 |
- image, err := srv.ImageGetCached(apiConfig.Id, apiConfig.Config) |
|
| 632 |
+ image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config) |
|
| 633 | 633 |
if err != nil {
|
| 634 | 634 |
return err |
| 635 | 635 |
} |
| ... | ... |
@@ -637,12 +661,12 @@ func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r * |
| 637 | 637 |
w.WriteHeader(http.StatusNotFound) |
| 638 | 638 |
return nil |
| 639 | 639 |
} |
| 640 |
- apiId := &ApiId{Id: image.Id}
|
|
| 641 |
- b, err := json.Marshal(apiId) |
|
| 640 |
+ apiID := &APIID{ID: image.ID}
|
|
| 641 |
+ b, err := json.Marshal(apiID) |
|
| 642 | 642 |
if err != nil {
|
| 643 | 643 |
return err |
| 644 | 644 |
} |
| 645 |
- writeJson(w, b) |
|
| 645 |
+ writeJSON(w, b) |
|
| 646 | 646 |
return nil |
| 647 | 647 |
} |
| 648 | 648 |
|
| ... | ... |
@@ -650,6 +674,13 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ |
| 650 | 650 |
if err := r.ParseMultipartForm(4096); err != nil {
|
| 651 | 651 |
return err |
| 652 | 652 |
} |
| 653 |
+ remote := r.FormValue("t")
|
|
| 654 |
+ tag := "" |
|
| 655 |
+ if strings.Contains(remote, ":") {
|
|
| 656 |
+ remoteParts := strings.Split(remote, ":") |
|
| 657 |
+ tag = remoteParts[1] |
|
| 658 |
+ remote = remoteParts[0] |
|
| 659 |
+ } |
|
| 653 | 660 |
|
| 654 | 661 |
dockerfile, _, err := r.FormFile("Dockerfile")
|
| 655 | 662 |
if err != nil {
|
| ... | ... |
@@ -664,8 +695,10 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ |
| 664 | 664 |
} |
| 665 | 665 |
|
| 666 | 666 |
b := NewBuildFile(srv, utils.NewWriteFlusher(w)) |
| 667 |
- if _, err := b.Build(dockerfile, context); err != nil {
|
|
| 667 |
+ if id, err := b.Build(dockerfile, context); err != nil {
|
|
| 668 | 668 |
fmt.Fprintf(w, "Error build: %s\n", err) |
| 669 |
+ } else if remote != "" {
|
|
| 670 |
+ srv.runtime.repositories.Set(remote, tag, id, false) |
|
| 669 | 671 |
} |
| 670 | 672 |
return nil |
| 671 | 673 |
} |
| ... | ... |
@@ -679,13 +712,13 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
|
| 679 | 679 |
"/auth": getAuth, |
| 680 | 680 |
"/version": getVersion, |
| 681 | 681 |
"/info": getInfo, |
| 682 |
- "/images/json": getImagesJson, |
|
| 682 |
+ "/images/json": getImagesJSON, |
|
| 683 | 683 |
"/images/viz": getImagesViz, |
| 684 | 684 |
"/images/search": getImagesSearch, |
| 685 | 685 |
"/images/{name:.*}/history": getImagesHistory,
|
| 686 | 686 |
"/images/{name:.*}/json": getImagesByName,
|
| 687 |
- "/containers/ps": getContainersJson, |
|
| 688 |
- "/containers/json": getContainersJson, |
|
| 687 |
+ "/containers/ps": getContainersJSON, |
|
| 688 |
+ "/containers/json": getContainersJSON, |
|
| 689 | 689 |
"/containers/{name:.*}/export": getContainersExport,
|
| 690 | 690 |
"/containers/{name:.*}/changes": getContainersChanges,
|
| 691 | 691 |
"/containers/{name:.*}/json": getContainersByName,
|
| ... | ... |
@@ -734,9 +767,9 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
|
| 734 | 734 |
} |
| 735 | 735 |
version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64) |
| 736 | 736 |
if err != nil {
|
| 737 |
- version = API_VERSION |
|
| 737 |
+ version = APIVERSION |
|
| 738 | 738 |
} |
| 739 |
- if version == 0 || version > API_VERSION {
|
|
| 739 |
+ if version == 0 || version > APIVERSION {
|
|
| 740 | 740 |
w.WriteHeader(http.StatusNotFound) |
| 741 | 741 |
return |
| 742 | 742 |
} |
| ... | ... |
@@ -1,75 +1,75 @@ |
| 1 | 1 |
package docker |
| 2 | 2 |
|
| 3 |
-type ApiHistory struct {
|
|
| 4 |
- Id string |
|
| 3 |
+type APIHistory struct {
|
|
| 4 |
+ ID string `json:"Id"` |
|
| 5 | 5 |
Created int64 |
| 6 |
- CreatedBy string |
|
| 6 |
+ CreatedBy string `json:",omitempty"` |
|
| 7 | 7 |
} |
| 8 | 8 |
|
| 9 |
-type ApiImages struct {
|
|
| 9 |
+type APIImages struct {
|
|
| 10 | 10 |
Repository string `json:",omitempty"` |
| 11 | 11 |
Tag string `json:",omitempty"` |
| 12 |
- Id string |
|
| 13 |
- Created int64 `json:",omitempty"` |
|
| 12 |
+ ID string `json:"Id"` |
|
| 13 |
+ Created int64 |
|
| 14 | 14 |
Size int64 |
| 15 | 15 |
ParentSize int64 |
| 16 |
+ |
|
| 16 | 17 |
} |
| 17 | 18 |
|
| 18 |
-type ApiInfo struct {
|
|
| 19 |
+type APIInfo struct {
|
|
| 20 |
+ Debug bool |
|
| 19 | 21 |
Containers int |
| 20 |
- Version string |
|
| 21 | 22 |
Images int |
| 22 |
- Debug bool |
|
| 23 |
- GoVersion string |
|
| 24 |
- NFd int `json:",omitempty"` |
|
| 25 |
- NGoroutines int `json:",omitempty"` |
|
| 23 |
+ NFd int `json:",omitempty"` |
|
| 24 |
+ NGoroutines int `json:",omitempty"` |
|
| 25 |
+ MemoryLimit bool `json:",omitempty"` |
|
| 26 |
+ SwapLimit bool `json:",omitempty"` |
|
| 26 | 27 |
} |
| 27 | 28 |
|
| 28 |
-type ApiContainers struct {
|
|
| 29 |
- Id string |
|
| 30 |
- Image string `json:",omitempty"` |
|
| 31 |
- Command string `json:",omitempty"` |
|
| 32 |
- Created int64 `json:",omitempty"` |
|
| 33 |
- Status string `json:",omitempty"` |
|
| 34 |
- Ports string `json:",omitempty"` |
|
| 29 |
+type APIContainers struct {
|
|
| 30 |
+ ID string `json:"Id"` |
|
| 31 |
+ Image string |
|
| 32 |
+ Command string |
|
| 33 |
+ Created int64 |
|
| 34 |
+ Status string |
|
| 35 |
+ Ports string |
|
| 35 | 36 |
SizeRw int64 |
| 36 | 37 |
SizeRootFs int64 |
| 37 | 38 |
} |
| 38 | 39 |
|
| 39 |
-type ApiSearch struct {
|
|
| 40 |
+type APISearch struct {
|
|
| 40 | 41 |
Name string |
| 41 | 42 |
Description string |
| 42 | 43 |
} |
| 43 | 44 |
|
| 44 |
-type ApiId struct {
|
|
| 45 |
- Id string |
|
| 45 |
+type APIID struct {
|
|
| 46 |
+ ID string `json:"Id"` |
|
| 46 | 47 |
} |
| 47 | 48 |
|
| 48 |
-type ApiRun struct {
|
|
| 49 |
- Id string |
|
| 50 |
- Warnings []string |
|
| 49 |
+type APIRun struct {
|
|
| 50 |
+ ID string `json:"Id"` |
|
| 51 |
+ Warnings []string `json:",omitempty"` |
|
| 51 | 52 |
} |
| 52 | 53 |
|
| 53 |
-type ApiPort struct {
|
|
| 54 |
+type APIPort struct {
|
|
| 54 | 55 |
Port string |
| 55 | 56 |
} |
| 56 | 57 |
|
| 57 |
-type ApiVersion struct {
|
|
| 58 |
- Version string |
|
| 59 |
- GitCommit string |
|
| 60 |
- MemoryLimit bool |
|
| 61 |
- SwapLimit bool |
|
| 58 |
+type APIVersion struct {
|
|
| 59 |
+ Version string |
|
| 60 |
+ GitCommit string `json:",omitempty"` |
|
| 61 |
+ GoVersion string `json:",omitempty"` |
|
| 62 | 62 |
} |
| 63 | 63 |
|
| 64 |
-type ApiWait struct {
|
|
| 64 |
+type APIWait struct {
|
|
| 65 | 65 |
StatusCode int |
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 |
-type ApiAuth struct {
|
|
| 68 |
+type APIAuth struct {
|
|
| 69 | 69 |
Status string |
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 |
-type ApiImageConfig struct {
|
|
| 73 |
- Id string |
|
| 72 |
+type APIImageConfig struct {
|
|
| 73 |
+ ID string `json:"Id"` |
|
| 74 | 74 |
*Config |
| 75 | 75 |
} |
| ... | ... |
@@ -37,17 +37,17 @@ func TestGetAuth(t *testing.T) {
|
| 37 | 37 |
Email: "utest@yopmail.com", |
| 38 | 38 |
} |
| 39 | 39 |
|
| 40 |
- authConfigJson, err := json.Marshal(authConfig) |
|
| 40 |
+ authConfigJSON, err := json.Marshal(authConfig) |
|
| 41 | 41 |
if err != nil {
|
| 42 | 42 |
t.Fatal(err) |
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 |
- req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJson))
|
|
| 45 |
+ req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJSON))
|
|
| 46 | 46 |
if err != nil {
|
| 47 | 47 |
t.Fatal(err) |
| 48 | 48 |
} |
| 49 | 49 |
|
| 50 |
- if err := postAuth(srv, API_VERSION, r, req, nil); err != nil {
|
|
| 50 |
+ if err := postAuth(srv, APIVERSION, r, req, nil); err != nil {
|
|
| 51 | 51 |
t.Fatal(err) |
| 52 | 52 |
} |
| 53 | 53 |
|
| ... | ... |
@@ -73,11 +73,11 @@ func TestGetVersion(t *testing.T) {
|
| 73 | 73 |
|
| 74 | 74 |
r := httptest.NewRecorder() |
| 75 | 75 |
|
| 76 |
- if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil {
|
|
| 76 |
+ if err := getVersion(srv, APIVERSION, r, nil, nil); err != nil {
|
|
| 77 | 77 |
t.Fatal(err) |
| 78 | 78 |
} |
| 79 | 79 |
|
| 80 |
- v := &ApiVersion{}
|
|
| 80 |
+ v := &APIVersion{}
|
|
| 81 | 81 |
if err = json.Unmarshal(r.Body.Bytes(), v); err != nil {
|
| 82 | 82 |
t.Fatal(err) |
| 83 | 83 |
} |
| ... | ... |
@@ -97,21 +97,21 @@ func TestGetInfo(t *testing.T) {
|
| 97 | 97 |
|
| 98 | 98 |
r := httptest.NewRecorder() |
| 99 | 99 |
|
| 100 |
- if err := getInfo(srv, API_VERSION, r, nil, nil); err != nil {
|
|
| 100 |
+ if err := getInfo(srv, APIVERSION, r, nil, nil); err != nil {
|
|
| 101 | 101 |
t.Fatal(err) |
| 102 | 102 |
} |
| 103 | 103 |
|
| 104 |
- infos := &ApiInfo{}
|
|
| 104 |
+ infos := &APIInfo{}
|
|
| 105 | 105 |
err = json.Unmarshal(r.Body.Bytes(), infos) |
| 106 | 106 |
if err != nil {
|
| 107 | 107 |
t.Fatal(err) |
| 108 | 108 |
} |
| 109 |
- if infos.Version != VERSION {
|
|
| 110 |
- t.Errorf("Excepted version %s, %s found", VERSION, infos.Version)
|
|
| 109 |
+ if infos.Images != 1 {
|
|
| 110 |
+ t.Errorf("Excepted images: %d, %d found", 1, infos.Images)
|
|
| 111 | 111 |
} |
| 112 | 112 |
} |
| 113 | 113 |
|
| 114 |
-func TestGetImagesJson(t *testing.T) {
|
|
| 114 |
+func TestGetImagesJSON(t *testing.T) {
|
|
| 115 | 115 |
runtime, err := newTestRuntime() |
| 116 | 116 |
if err != nil {
|
| 117 | 117 |
t.Fatal(err) |
| ... | ... |
@@ -128,11 +128,11 @@ func TestGetImagesJson(t *testing.T) {
|
| 128 | 128 |
|
| 129 | 129 |
r := httptest.NewRecorder() |
| 130 | 130 |
|
| 131 |
- if err := getImagesJson(srv, API_VERSION, r, req, nil); err != nil {
|
|
| 131 |
+ if err := getImagesJSON(srv, APIVERSION, r, req, nil); err != nil {
|
|
| 132 | 132 |
t.Fatal(err) |
| 133 | 133 |
} |
| 134 | 134 |
|
| 135 |
- images := []ApiImages{}
|
|
| 135 |
+ images := []APIImages{}
|
|
| 136 | 136 |
if err := json.Unmarshal(r.Body.Bytes(), &images); err != nil {
|
| 137 | 137 |
t.Fatal(err) |
| 138 | 138 |
} |
| ... | ... |
@@ -153,11 +153,11 @@ func TestGetImagesJson(t *testing.T) {
|
| 153 | 153 |
t.Fatal(err) |
| 154 | 154 |
} |
| 155 | 155 |
|
| 156 |
- if err := getImagesJson(srv, API_VERSION, r2, req2, nil); err != nil {
|
|
| 156 |
+ if err := getImagesJSON(srv, APIVERSION, r2, req2, nil); err != nil {
|
|
| 157 | 157 |
t.Fatal(err) |
| 158 | 158 |
} |
| 159 | 159 |
|
| 160 |
- images2 := []ApiImages{}
|
|
| 160 |
+ images2 := []APIImages{}
|
|
| 161 | 161 |
if err := json.Unmarshal(r2.Body.Bytes(), &images2); err != nil {
|
| 162 | 162 |
t.Fatal(err) |
| 163 | 163 |
} |
| ... | ... |
@@ -166,8 +166,8 @@ func TestGetImagesJson(t *testing.T) {
|
| 166 | 166 |
t.Errorf("Excepted 1 image, %d found", len(images2))
|
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 |
- if images2[0].Id != GetTestImage(runtime).Id {
|
|
| 170 |
- t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).Id, images2[0].Id)
|
|
| 169 |
+ if images2[0].ID != GetTestImage(runtime).ID {
|
|
| 170 |
+ t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).ID, images2[0].ID)
|
|
| 171 | 171 |
} |
| 172 | 172 |
|
| 173 | 173 |
r3 := httptest.NewRecorder() |
| ... | ... |
@@ -178,11 +178,11 @@ func TestGetImagesJson(t *testing.T) {
|
| 178 | 178 |
t.Fatal(err) |
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 |
- if err := getImagesJson(srv, API_VERSION, r3, req3, nil); err != nil {
|
|
| 181 |
+ if err := getImagesJSON(srv, APIVERSION, r3, req3, nil); err != nil {
|
|
| 182 | 182 |
t.Fatal(err) |
| 183 | 183 |
} |
| 184 | 184 |
|
| 185 |
- images3 := []ApiImages{}
|
|
| 185 |
+ images3 := []APIImages{}
|
|
| 186 | 186 |
if err := json.Unmarshal(r3.Body.Bytes(), &images3); err != nil {
|
| 187 | 187 |
t.Fatal(err) |
| 188 | 188 |
} |
| ... | ... |
@@ -199,7 +199,7 @@ func TestGetImagesJson(t *testing.T) {
|
| 199 | 199 |
t.Fatal(err) |
| 200 | 200 |
} |
| 201 | 201 |
|
| 202 |
- err = getImagesJson(srv, API_VERSION, r4, req4, nil) |
|
| 202 |
+ err = getImagesJSON(srv, APIVERSION, r4, req4, nil) |
|
| 203 | 203 |
if err == nil {
|
| 204 | 204 |
t.Fatalf("Error expected, received none")
|
| 205 | 205 |
} |
| ... | ... |
@@ -220,7 +220,7 @@ func TestGetImagesViz(t *testing.T) {
|
| 220 | 220 |
srv := &Server{runtime: runtime}
|
| 221 | 221 |
|
| 222 | 222 |
r := httptest.NewRecorder() |
| 223 |
- if err := getImagesViz(srv, API_VERSION, r, nil, nil); err != nil {
|
|
| 223 |
+ if err := getImagesViz(srv, APIVERSION, r, nil, nil); err != nil {
|
|
| 224 | 224 |
t.Fatal(err) |
| 225 | 225 |
} |
| 226 | 226 |
|
| ... | ... |
@@ -256,11 +256,11 @@ func TestGetImagesSearch(t *testing.T) {
|
| 256 | 256 |
t.Fatal(err) |
| 257 | 257 |
} |
| 258 | 258 |
|
| 259 |
- if err := getImagesSearch(srv, API_VERSION, r, req, nil); err != nil {
|
|
| 259 |
+ if err := getImagesSearch(srv, APIVERSION, r, req, nil); err != nil {
|
|
| 260 | 260 |
t.Fatal(err) |
| 261 | 261 |
} |
| 262 | 262 |
|
| 263 |
- results := []ApiSearch{}
|
|
| 263 |
+ results := []APISearch{}
|
|
| 264 | 264 |
if err := json.Unmarshal(r.Body.Bytes(), &results); err != nil {
|
| 265 | 265 |
t.Fatal(err) |
| 266 | 266 |
} |
| ... | ... |
@@ -280,11 +280,11 @@ func TestGetImagesHistory(t *testing.T) {
|
| 280 | 280 |
|
| 281 | 281 |
r := httptest.NewRecorder() |
| 282 | 282 |
|
| 283 |
- if err := getImagesHistory(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
|
|
| 283 |
+ if err := getImagesHistory(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
|
|
| 284 | 284 |
t.Fatal(err) |
| 285 | 285 |
} |
| 286 | 286 |
|
| 287 |
- history := []ApiHistory{}
|
|
| 287 |
+ history := []APIHistory{}
|
|
| 288 | 288 |
if err := json.Unmarshal(r.Body.Bytes(), &history); err != nil {
|
| 289 | 289 |
t.Fatal(err) |
| 290 | 290 |
} |
| ... | ... |
@@ -303,7 +303,7 @@ func TestGetImagesByName(t *testing.T) {
|
| 303 | 303 |
srv := &Server{runtime: runtime}
|
| 304 | 304 |
|
| 305 | 305 |
r := httptest.NewRecorder() |
| 306 |
- if err := getImagesByName(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
|
|
| 306 |
+ if err := getImagesByName(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
|
|
| 307 | 307 |
t.Fatal(err) |
| 308 | 308 |
} |
| 309 | 309 |
|
| ... | ... |
@@ -311,12 +311,12 @@ func TestGetImagesByName(t *testing.T) {
|
| 311 | 311 |
if err := json.Unmarshal(r.Body.Bytes(), img); err != nil {
|
| 312 | 312 |
t.Fatal(err) |
| 313 | 313 |
} |
| 314 |
- if img.Id != GetTestImage(runtime).Id || img.Comment != "Imported from http://get.docker.io/images/busybox" {
|
|
| 314 |
+ if img.ID != GetTestImage(runtime).ID || img.Comment != "Imported from http://get.docker.io/images/busybox" {
|
|
| 315 | 315 |
t.Errorf("Error inspecting image")
|
| 316 | 316 |
} |
| 317 | 317 |
} |
| 318 | 318 |
|
| 319 |
-func TestGetContainersJson(t *testing.T) {
|
|
| 319 |
+func TestGetContainersJSON(t *testing.T) {
|
|
| 320 | 320 |
runtime, err := newTestRuntime() |
| 321 | 321 |
if err != nil {
|
| 322 | 322 |
t.Fatal(err) |
| ... | ... |
@@ -326,7 +326,7 @@ func TestGetContainersJson(t *testing.T) {
|
| 326 | 326 |
srv := &Server{runtime: runtime}
|
| 327 | 327 |
|
| 328 | 328 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 329 |
- Image: GetTestImage(runtime).Id, |
|
| 329 |
+ Image: GetTestImage(runtime).ID, |
|
| 330 | 330 |
Cmd: []string{"echo", "test"},
|
| 331 | 331 |
}) |
| 332 | 332 |
if err != nil {
|
| ... | ... |
@@ -340,18 +340,18 @@ func TestGetContainersJson(t *testing.T) {
|
| 340 | 340 |
} |
| 341 | 341 |
|
| 342 | 342 |
r := httptest.NewRecorder() |
| 343 |
- if err := getContainersJson(srv, API_VERSION, r, req, nil); err != nil {
|
|
| 343 |
+ if err := getContainersJSON(srv, APIVERSION, r, req, nil); err != nil {
|
|
| 344 | 344 |
t.Fatal(err) |
| 345 | 345 |
} |
| 346 |
- containers := []ApiContainers{}
|
|
| 346 |
+ containers := []APIContainers{}
|
|
| 347 | 347 |
if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
|
| 348 | 348 |
t.Fatal(err) |
| 349 | 349 |
} |
| 350 | 350 |
if len(containers) != 1 {
|
| 351 | 351 |
t.Fatalf("Excepted %d container, %d found", 1, len(containers))
|
| 352 | 352 |
} |
| 353 |
- if containers[0].Id != container.Id {
|
|
| 354 |
- t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.Id, containers[0].Id)
|
|
| 353 |
+ if containers[0].ID != container.ID {
|
|
| 354 |
+ t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.ID, containers[0].ID)
|
|
| 355 | 355 |
} |
| 356 | 356 |
} |
| 357 | 357 |
|
| ... | ... |
@@ -369,7 +369,7 @@ func TestGetContainersExport(t *testing.T) {
|
| 369 | 369 |
// Create a container and remove a file |
| 370 | 370 |
container, err := builder.Create( |
| 371 | 371 |
&Config{
|
| 372 |
- Image: GetTestImage(runtime).Id, |
|
| 372 |
+ Image: GetTestImage(runtime).ID, |
|
| 373 | 373 |
Cmd: []string{"touch", "/test"},
|
| 374 | 374 |
}, |
| 375 | 375 |
) |
| ... | ... |
@@ -383,7 +383,7 @@ func TestGetContainersExport(t *testing.T) {
|
| 383 | 383 |
} |
| 384 | 384 |
|
| 385 | 385 |
r := httptest.NewRecorder() |
| 386 |
- if err = getContainersExport(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
|
| 386 |
+ if err = getContainersExport(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
|
|
| 387 | 387 |
t.Fatal(err) |
| 388 | 388 |
} |
| 389 | 389 |
|
| ... | ... |
@@ -424,7 +424,7 @@ func TestGetContainersChanges(t *testing.T) {
|
| 424 | 424 |
// Create a container and remove a file |
| 425 | 425 |
container, err := builder.Create( |
| 426 | 426 |
&Config{
|
| 427 |
- Image: GetTestImage(runtime).Id, |
|
| 427 |
+ Image: GetTestImage(runtime).ID, |
|
| 428 | 428 |
Cmd: []string{"/bin/rm", "/etc/passwd"},
|
| 429 | 429 |
}, |
| 430 | 430 |
) |
| ... | ... |
@@ -438,7 +438,7 @@ func TestGetContainersChanges(t *testing.T) {
|
| 438 | 438 |
} |
| 439 | 439 |
|
| 440 | 440 |
r := httptest.NewRecorder() |
| 441 |
- if err := getContainersChanges(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
|
| 441 |
+ if err := getContainersChanges(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
|
|
| 442 | 442 |
t.Fatal(err) |
| 443 | 443 |
} |
| 444 | 444 |
changes := []Change{}
|
| ... | ... |
@@ -472,7 +472,7 @@ func TestGetContainersByName(t *testing.T) {
|
| 472 | 472 |
// Create a container and remove a file |
| 473 | 473 |
container, err := builder.Create( |
| 474 | 474 |
&Config{
|
| 475 |
- Image: GetTestImage(runtime).Id, |
|
| 475 |
+ Image: GetTestImage(runtime).ID, |
|
| 476 | 476 |
Cmd: []string{"echo", "test"},
|
| 477 | 477 |
}, |
| 478 | 478 |
) |
| ... | ... |
@@ -482,15 +482,15 @@ func TestGetContainersByName(t *testing.T) {
|
| 482 | 482 |
defer runtime.Destroy(container) |
| 483 | 483 |
|
| 484 | 484 |
r := httptest.NewRecorder() |
| 485 |
- if err := getContainersByName(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
|
| 485 |
+ if err := getContainersByName(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
|
|
| 486 | 486 |
t.Fatal(err) |
| 487 | 487 |
} |
| 488 | 488 |
outContainer := &Container{}
|
| 489 | 489 |
if err := json.Unmarshal(r.Body.Bytes(), outContainer); err != nil {
|
| 490 | 490 |
t.Fatal(err) |
| 491 | 491 |
} |
| 492 |
- if outContainer.Id != container.Id {
|
|
| 493 |
- t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.Id, outContainer.Id)
|
|
| 492 |
+ if outContainer.ID != container.ID {
|
|
| 493 |
+ t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.ID, outContainer.ID)
|
|
| 494 | 494 |
} |
| 495 | 495 |
} |
| 496 | 496 |
|
| ... | ... |
@@ -514,7 +514,7 @@ func TestPostAuth(t *testing.T) {
|
| 514 | 514 |
auth.SaveConfig(runtime.root, authStr, config.Email) |
| 515 | 515 |
|
| 516 | 516 |
r := httptest.NewRecorder() |
| 517 |
- if err := getAuth(srv, API_VERSION, r, nil, nil); err != nil {
|
|
| 517 |
+ if err := getAuth(srv, APIVERSION, r, nil, nil); err != nil {
|
|
| 518 | 518 |
t.Fatal(err) |
| 519 | 519 |
} |
| 520 | 520 |
|
| ... | ... |
@@ -542,7 +542,7 @@ func TestPostCommit(t *testing.T) {
|
| 542 | 542 |
// Create a container and remove a file |
| 543 | 543 |
container, err := builder.Create( |
| 544 | 544 |
&Config{
|
| 545 |
- Image: GetTestImage(runtime).Id, |
|
| 545 |
+ Image: GetTestImage(runtime).ID, |
|
| 546 | 546 |
Cmd: []string{"touch", "/test"},
|
| 547 | 547 |
}, |
| 548 | 548 |
) |
| ... | ... |
@@ -555,24 +555,24 @@ func TestPostCommit(t *testing.T) {
|
| 555 | 555 |
t.Fatal(err) |
| 556 | 556 |
} |
| 557 | 557 |
|
| 558 |
- req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.Id, bytes.NewReader([]byte{}))
|
|
| 558 |
+ req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.ID, bytes.NewReader([]byte{}))
|
|
| 559 | 559 |
if err != nil {
|
| 560 | 560 |
t.Fatal(err) |
| 561 | 561 |
} |
| 562 | 562 |
|
| 563 | 563 |
r := httptest.NewRecorder() |
| 564 |
- if err := postCommit(srv, API_VERSION, r, req, nil); err != nil {
|
|
| 564 |
+ if err := postCommit(srv, APIVERSION, r, req, nil); err != nil {
|
|
| 565 | 565 |
t.Fatal(err) |
| 566 | 566 |
} |
| 567 | 567 |
if r.Code != http.StatusCreated {
|
| 568 | 568 |
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
|
| 569 | 569 |
} |
| 570 | 570 |
|
| 571 |
- apiId := &ApiId{}
|
|
| 572 |
- if err := json.Unmarshal(r.Body.Bytes(), apiId); err != nil {
|
|
| 571 |
+ apiID := &APIID{}
|
|
| 572 |
+ if err := json.Unmarshal(r.Body.Bytes(), apiID); err != nil {
|
|
| 573 | 573 |
t.Fatal(err) |
| 574 | 574 |
} |
| 575 |
- if _, err := runtime.graph.Get(apiId.Id); err != nil {
|
|
| 575 |
+ if _, err := runtime.graph.Get(apiID.ID); err != nil {
|
|
| 576 | 576 |
t.Fatalf("The image has not been commited")
|
| 577 | 577 |
} |
| 578 | 578 |
} |
| ... | ... |
@@ -715,7 +715,7 @@ func TestPostImagesInsert(t *testing.T) {
|
| 715 | 715 |
// t.Fatalf("The test file has not been found")
|
| 716 | 716 |
// } |
| 717 | 717 |
|
| 718 |
- // if err := srv.runtime.graph.Delete(img.Id); err != nil {
|
|
| 718 |
+ // if err := srv.runtime.graph.Delete(img.ID); err != nil {
|
|
| 719 | 719 |
// t.Fatal(err) |
| 720 | 720 |
// } |
| 721 | 721 |
} |
| ... | ... |
@@ -824,8 +824,8 @@ func TestPostContainersCreate(t *testing.T) {
|
| 824 | 824 |
|
| 825 | 825 |
srv := &Server{runtime: runtime}
|
| 826 | 826 |
|
| 827 |
- configJson, err := json.Marshal(&Config{
|
|
| 828 |
- Image: GetTestImage(runtime).Id, |
|
| 827 |
+ configJSON, err := json.Marshal(&Config{
|
|
| 828 |
+ Image: GetTestImage(runtime).ID, |
|
| 829 | 829 |
Memory: 33554432, |
| 830 | 830 |
Cmd: []string{"touch", "/test"},
|
| 831 | 831 |
}) |
| ... | ... |
@@ -833,25 +833,25 @@ func TestPostContainersCreate(t *testing.T) {
|
| 833 | 833 |
t.Fatal(err) |
| 834 | 834 |
} |
| 835 | 835 |
|
| 836 |
- req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJson))
|
|
| 836 |
+ req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJSON))
|
|
| 837 | 837 |
if err != nil {
|
| 838 | 838 |
t.Fatal(err) |
| 839 | 839 |
} |
| 840 | 840 |
|
| 841 | 841 |
r := httptest.NewRecorder() |
| 842 |
- if err := postContainersCreate(srv, API_VERSION, r, req, nil); err != nil {
|
|
| 842 |
+ if err := postContainersCreate(srv, APIVERSION, r, req, nil); err != nil {
|
|
| 843 | 843 |
t.Fatal(err) |
| 844 | 844 |
} |
| 845 | 845 |
if r.Code != http.StatusCreated {
|
| 846 | 846 |
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
|
| 847 | 847 |
} |
| 848 | 848 |
|
| 849 |
- apiRun := &ApiRun{}
|
|
| 849 |
+ apiRun := &APIRun{}
|
|
| 850 | 850 |
if err := json.Unmarshal(r.Body.Bytes(), apiRun); err != nil {
|
| 851 | 851 |
t.Fatal(err) |
| 852 | 852 |
} |
| 853 | 853 |
|
| 854 |
- container := srv.runtime.Get(apiRun.Id) |
|
| 854 |
+ container := srv.runtime.Get(apiRun.ID) |
|
| 855 | 855 |
if container == nil {
|
| 856 | 856 |
t.Fatalf("Container not created")
|
| 857 | 857 |
} |
| ... | ... |
@@ -880,7 +880,7 @@ func TestPostContainersKill(t *testing.T) {
|
| 880 | 880 |
|
| 881 | 881 |
container, err := NewBuilder(runtime).Create( |
| 882 | 882 |
&Config{
|
| 883 |
- Image: GetTestImage(runtime).Id, |
|
| 883 |
+ Image: GetTestImage(runtime).ID, |
|
| 884 | 884 |
Cmd: []string{"/bin/cat"},
|
| 885 | 885 |
OpenStdin: true, |
| 886 | 886 |
}, |
| ... | ... |
@@ -902,7 +902,7 @@ func TestPostContainersKill(t *testing.T) {
|
| 902 | 902 |
} |
| 903 | 903 |
|
| 904 | 904 |
r := httptest.NewRecorder() |
| 905 |
- if err := postContainersKill(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
|
| 905 |
+ if err := postContainersKill(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
|
|
| 906 | 906 |
t.Fatal(err) |
| 907 | 907 |
} |
| 908 | 908 |
if r.Code != http.StatusNoContent {
|
| ... | ... |
@@ -924,7 +924,7 @@ func TestPostContainersRestart(t *testing.T) {
|
| 924 | 924 |
|
| 925 | 925 |
container, err := NewBuilder(runtime).Create( |
| 926 | 926 |
&Config{
|
| 927 |
- Image: GetTestImage(runtime).Id, |
|
| 927 |
+ Image: GetTestImage(runtime).ID, |
|
| 928 | 928 |
Cmd: []string{"/bin/cat"},
|
| 929 | 929 |
OpenStdin: true, |
| 930 | 930 |
}, |
| ... | ... |
@@ -945,12 +945,12 @@ func TestPostContainersRestart(t *testing.T) {
|
| 945 | 945 |
t.Errorf("Container should be running")
|
| 946 | 946 |
} |
| 947 | 947 |
|
| 948 |
- req, err := http.NewRequest("POST", "/containers/"+container.Id+"/restart?t=1", bytes.NewReader([]byte{}))
|
|
| 948 |
+ req, err := http.NewRequest("POST", "/containers/"+container.ID+"/restart?t=1", bytes.NewReader([]byte{}))
|
|
| 949 | 949 |
if err != nil {
|
| 950 | 950 |
t.Fatal(err) |
| 951 | 951 |
} |
| 952 | 952 |
r := httptest.NewRecorder() |
| 953 |
- if err := postContainersRestart(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
|
|
| 953 |
+ if err := postContainersRestart(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
|
|
| 954 | 954 |
t.Fatal(err) |
| 955 | 955 |
} |
| 956 | 956 |
if r.Code != http.StatusNoContent {
|
| ... | ... |
@@ -980,7 +980,7 @@ func TestPostContainersStart(t *testing.T) {
|
| 980 | 980 |
|
| 981 | 981 |
container, err := NewBuilder(runtime).Create( |
| 982 | 982 |
&Config{
|
| 983 |
- Image: GetTestImage(runtime).Id, |
|
| 983 |
+ Image: GetTestImage(runtime).ID, |
|
| 984 | 984 |
Cmd: []string{"/bin/cat"},
|
| 985 | 985 |
OpenStdin: true, |
| 986 | 986 |
}, |
| ... | ... |
@@ -991,7 +991,7 @@ func TestPostContainersStart(t *testing.T) {
|
| 991 | 991 |
defer runtime.Destroy(container) |
| 992 | 992 |
|
| 993 | 993 |
r := httptest.NewRecorder() |
| 994 |
- if err := postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
|
| 994 |
+ if err := postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
|
|
| 995 | 995 |
t.Fatal(err) |
| 996 | 996 |
} |
| 997 | 997 |
if r.Code != http.StatusNoContent {
|
| ... | ... |
@@ -1006,7 +1006,7 @@ func TestPostContainersStart(t *testing.T) {
|
| 1006 | 1006 |
} |
| 1007 | 1007 |
|
| 1008 | 1008 |
r = httptest.NewRecorder() |
| 1009 |
- if err = postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err == nil {
|
|
| 1009 |
+ if err = postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err == nil {
|
|
| 1010 | 1010 |
t.Fatalf("A running containter should be able to be started")
|
| 1011 | 1011 |
} |
| 1012 | 1012 |
|
| ... | ... |
@@ -1026,7 +1026,7 @@ func TestPostContainersStop(t *testing.T) {
|
| 1026 | 1026 |
|
| 1027 | 1027 |
container, err := NewBuilder(runtime).Create( |
| 1028 | 1028 |
&Config{
|
| 1029 |
- Image: GetTestImage(runtime).Id, |
|
| 1029 |
+ Image: GetTestImage(runtime).ID, |
|
| 1030 | 1030 |
Cmd: []string{"/bin/cat"},
|
| 1031 | 1031 |
OpenStdin: true, |
| 1032 | 1032 |
}, |
| ... | ... |
@@ -1048,12 +1048,12 @@ func TestPostContainersStop(t *testing.T) {
|
| 1048 | 1048 |
} |
| 1049 | 1049 |
|
| 1050 | 1050 |
// Note: as it is a POST request, it requires a body. |
| 1051 |
- req, err := http.NewRequest("POST", "/containers/"+container.Id+"/stop?t=1", bytes.NewReader([]byte{}))
|
|
| 1051 |
+ req, err := http.NewRequest("POST", "/containers/"+container.ID+"/stop?t=1", bytes.NewReader([]byte{}))
|
|
| 1052 | 1052 |
if err != nil {
|
| 1053 | 1053 |
t.Fatal(err) |
| 1054 | 1054 |
} |
| 1055 | 1055 |
r := httptest.NewRecorder() |
| 1056 |
- if err := postContainersStop(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
|
|
| 1056 |
+ if err := postContainersStop(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
|
|
| 1057 | 1057 |
t.Fatal(err) |
| 1058 | 1058 |
} |
| 1059 | 1059 |
if r.Code != http.StatusNoContent {
|
| ... | ... |
@@ -1075,7 +1075,7 @@ func TestPostContainersWait(t *testing.T) {
|
| 1075 | 1075 |
|
| 1076 | 1076 |
container, err := NewBuilder(runtime).Create( |
| 1077 | 1077 |
&Config{
|
| 1078 |
- Image: GetTestImage(runtime).Id, |
|
| 1078 |
+ Image: GetTestImage(runtime).ID, |
|
| 1079 | 1079 |
Cmd: []string{"/bin/sleep", "1"},
|
| 1080 | 1080 |
OpenStdin: true, |
| 1081 | 1081 |
}, |
| ... | ... |
@@ -1091,10 +1091,10 @@ func TestPostContainersWait(t *testing.T) {
|
| 1091 | 1091 |
|
| 1092 | 1092 |
setTimeout(t, "Wait timed out", 3*time.Second, func() {
|
| 1093 | 1093 |
r := httptest.NewRecorder() |
| 1094 |
- if err := postContainersWait(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
|
| 1094 |
+ if err := postContainersWait(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
|
|
| 1095 | 1095 |
t.Fatal(err) |
| 1096 | 1096 |
} |
| 1097 |
- apiWait := &ApiWait{}
|
|
| 1097 |
+ apiWait := &APIWait{}
|
|
| 1098 | 1098 |
if err := json.Unmarshal(r.Body.Bytes(), apiWait); err != nil {
|
| 1099 | 1099 |
t.Fatal(err) |
| 1100 | 1100 |
} |
| ... | ... |
@@ -1119,7 +1119,7 @@ func TestPostContainersAttach(t *testing.T) {
|
| 1119 | 1119 |
|
| 1120 | 1120 |
container, err := NewBuilder(runtime).Create( |
| 1121 | 1121 |
&Config{
|
| 1122 |
- Image: GetTestImage(runtime).Id, |
|
| 1122 |
+ Image: GetTestImage(runtime).ID, |
|
| 1123 | 1123 |
Cmd: []string{"/bin/cat"},
|
| 1124 | 1124 |
OpenStdin: true, |
| 1125 | 1125 |
}, |
| ... | ... |
@@ -1148,12 +1148,12 @@ func TestPostContainersAttach(t *testing.T) {
|
| 1148 | 1148 |
out: stdoutPipe, |
| 1149 | 1149 |
} |
| 1150 | 1150 |
|
| 1151 |
- req, err := http.NewRequest("POST", "/containers/"+container.Id+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
|
|
| 1151 |
+ req, err := http.NewRequest("POST", "/containers/"+container.ID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
|
|
| 1152 | 1152 |
if err != nil {
|
| 1153 | 1153 |
t.Fatal(err) |
| 1154 | 1154 |
} |
| 1155 | 1155 |
|
| 1156 |
- if err := postContainersAttach(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
|
|
| 1156 |
+ if err := postContainersAttach(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
|
|
| 1157 | 1157 |
t.Fatal(err) |
| 1158 | 1158 |
} |
| 1159 | 1159 |
}() |
| ... | ... |
@@ -1206,7 +1206,7 @@ func TestDeleteContainers(t *testing.T) {
|
| 1206 | 1206 |
srv := &Server{runtime: runtime}
|
| 1207 | 1207 |
|
| 1208 | 1208 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 1209 |
- Image: GetTestImage(runtime).Id, |
|
| 1209 |
+ Image: GetTestImage(runtime).ID, |
|
| 1210 | 1210 |
Cmd: []string{"touch", "/test"},
|
| 1211 | 1211 |
}) |
| 1212 | 1212 |
if err != nil {
|
| ... | ... |
@@ -1218,19 +1218,19 @@ func TestDeleteContainers(t *testing.T) {
|
| 1218 | 1218 |
t.Fatal(err) |
| 1219 | 1219 |
} |
| 1220 | 1220 |
|
| 1221 |
- req, err := http.NewRequest("DELETE", "/containers/"+container.Id, nil)
|
|
| 1221 |
+ req, err := http.NewRequest("DELETE", "/containers/"+container.ID, nil)
|
|
| 1222 | 1222 |
if err != nil {
|
| 1223 | 1223 |
t.Fatal(err) |
| 1224 | 1224 |
} |
| 1225 | 1225 |
r := httptest.NewRecorder() |
| 1226 |
- if err := deleteContainers(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
|
|
| 1226 |
+ if err := deleteContainers(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
|
|
| 1227 | 1227 |
t.Fatal(err) |
| 1228 | 1228 |
} |
| 1229 | 1229 |
if r.Code != http.StatusNoContent {
|
| 1230 | 1230 |
t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
|
| 1231 | 1231 |
} |
| 1232 | 1232 |
|
| 1233 |
- if c := runtime.Get(container.Id); c != nil {
|
|
| 1233 |
+ if c := runtime.Get(container.ID); c != nil {
|
|
| 1234 | 1234 |
t.Fatalf("The container as not been deleted")
|
| 1235 | 1235 |
} |
| 1236 | 1236 |
|
| ... | ... |
@@ -54,6 +54,9 @@ func Tar(path string, compression Compression) (io.Reader, error) {
|
| 54 | 54 |
func Untar(archive io.Reader, path string) error {
|
| 55 | 55 |
cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
|
| 56 | 56 |
cmd.Stdin = archive |
| 57 |
+ // Hardcode locale environment for predictable outcome regardless of host configuration. |
|
| 58 |
+ // (see https://github.com/dotcloud/docker/issues/355) |
|
| 59 |
+ cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
|
|
| 57 | 60 |
output, err := cmd.CombinedOutput() |
| 58 | 61 |
if err != nil {
|
| 59 | 62 |
return fmt.Errorf("%s: %s", err, output)
|
| ... | ... |
@@ -16,12 +16,12 @@ import ( |
| 16 | 16 |
const CONFIGFILE = ".dockercfg" |
| 17 | 17 |
|
| 18 | 18 |
// the registry server we want to login against |
| 19 |
-const INDEX_SERVER = "https://index.docker.io/v1" |
|
| 19 |
+const INDEXSERVER = "https://index.docker.io/v1" |
|
| 20 | 20 |
|
| 21 |
-//const INDEX_SERVER = "http://indexstaging-docker.dotcloud.com/" |
|
| 21 |
+//const INDEXSERVER = "http://indexstaging-docker.dotcloud.com/" |
|
| 22 | 22 |
|
| 23 | 23 |
var ( |
| 24 |
- ErrConfigFileMissing error = errors.New("The Auth config file is missing")
|
|
| 24 |
+ ErrConfigFileMissing = errors.New("The Auth config file is missing")
|
|
| 25 | 25 |
) |
| 26 | 26 |
|
| 27 | 27 |
type AuthConfig struct {
|
| ... | ... |
@@ -44,7 +44,7 @@ func IndexServerAddress() string {
|
| 44 | 44 |
if os.Getenv("DOCKER_INDEX_URL") != "" {
|
| 45 | 45 |
return os.Getenv("DOCKER_INDEX_URL") + "/v1"
|
| 46 | 46 |
} |
| 47 |
- return INDEX_SERVER |
|
| 47 |
+ return INDEXSERVER |
|
| 48 | 48 |
} |
| 49 | 49 |
|
| 50 | 50 |
// create a base64 encoded auth string to store in config |
| ... | ... |
@@ -40,7 +40,7 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
|
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 | 42 |
// Generate id |
| 43 |
- id := GenerateId() |
|
| 43 |
+ id := GenerateID() |
|
| 44 | 44 |
// Generate default hostname |
| 45 | 45 |
// FIXME: the lxc template no longer needs to set a default hostname |
| 46 | 46 |
if config.Hostname == "" {
|
| ... | ... |
@@ -49,17 +49,17 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
|
| 49 | 49 |
|
| 50 | 50 |
container := &Container{
|
| 51 | 51 |
// FIXME: we should generate the ID here instead of receiving it as an argument |
| 52 |
- Id: id, |
|
| 52 |
+ ID: id, |
|
| 53 | 53 |
Created: time.Now(), |
| 54 | 54 |
Path: config.Cmd[0], |
| 55 | 55 |
Args: config.Cmd[1:], //FIXME: de-duplicate from config |
| 56 | 56 |
Config: config, |
| 57 |
- Image: img.Id, // Always use the resolved image id |
|
| 57 |
+ Image: img.ID, // Always use the resolved image id |
|
| 58 | 58 |
NetworkSettings: &NetworkSettings{},
|
| 59 | 59 |
// FIXME: do we need to store this in the container? |
| 60 | 60 |
SysInitPath: sysInitPath, |
| 61 | 61 |
} |
| 62 |
- container.root = builder.runtime.containerRoot(container.Id) |
|
| 62 |
+ container.root = builder.runtime.containerRoot(container.ID) |
|
| 63 | 63 |
// Step 1: create the container directory. |
| 64 | 64 |
// This doubles as a barrier to avoid race conditions. |
| 65 | 65 |
if err := os.Mkdir(container.root, 0700); err != nil {
|
| ... | ... |
@@ -110,7 +110,7 @@ func (builder *Builder) Commit(container *Container, repository, tag, comment, a |
| 110 | 110 |
} |
| 111 | 111 |
// Register the image if needed |
| 112 | 112 |
if repository != "" {
|
| 113 |
- if err := builder.repositories.Set(repository, tag, img.Id, true); err != nil {
|
|
| 113 |
+ if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil {
|
|
| 114 | 114 |
return img, err |
| 115 | 115 |
} |
| 116 | 116 |
} |
| ... | ... |
@@ -63,11 +63,11 @@ func (b *builderClient) CmdFrom(name string) error {
|
| 63 | 63 |
return err |
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
- img := &ApiId{}
|
|
| 66 |
+ img := &APIID{}
|
|
| 67 | 67 |
if err := json.Unmarshal(obj, img); err != nil {
|
| 68 | 68 |
return err |
| 69 | 69 |
} |
| 70 |
- b.image = img.Id |
|
| 70 |
+ b.image = img.ID |
|
| 71 | 71 |
utils.Debugf("Using image %s", b.image)
|
| 72 | 72 |
return nil |
| 73 | 73 |
} |
| ... | ... |
@@ -91,19 +91,19 @@ func (b *builderClient) CmdRun(args string) error {
|
| 91 | 91 |
b.config.Cmd = nil |
| 92 | 92 |
MergeConfig(b.config, config) |
| 93 | 93 |
|
| 94 |
- body, statusCode, err := b.cli.call("POST", "/images/getCache", &ApiImageConfig{Id: b.image, Config: b.config})
|
|
| 94 |
+ body, statusCode, err := b.cli.call("POST", "/images/getCache", &APIImageConfig{ID: b.image, Config: b.config})
|
|
| 95 | 95 |
if err != nil {
|
| 96 | 96 |
if statusCode != 404 {
|
| 97 | 97 |
return err |
| 98 | 98 |
} |
| 99 | 99 |
} |
| 100 | 100 |
if statusCode != 404 {
|
| 101 |
- apiId := &ApiId{}
|
|
| 102 |
- if err := json.Unmarshal(body, apiId); err != nil {
|
|
| 101 |
+ apiID := &APIID{}
|
|
| 102 |
+ if err := json.Unmarshal(body, apiID); err != nil {
|
|
| 103 | 103 |
return err |
| 104 | 104 |
} |
| 105 | 105 |
utils.Debugf("Use cached version")
|
| 106 |
- b.image = apiId.Id |
|
| 106 |
+ b.image = apiID.ID |
|
| 107 | 107 |
return nil |
| 108 | 108 |
} |
| 109 | 109 |
cid, err := b.run() |
| ... | ... |
@@ -163,7 +163,7 @@ func (b *builderClient) CmdInsert(args string) error {
|
| 163 | 163 |
// return err |
| 164 | 164 |
// } |
| 165 | 165 |
|
| 166 |
- // apiId := &ApiId{}
|
|
| 166 |
+ // apiId := &APIId{}
|
|
| 167 | 167 |
// if err := json.Unmarshal(body, apiId); err != nil {
|
| 168 | 168 |
// return err |
| 169 | 169 |
// } |
| ... | ... |
@@ -182,7 +182,7 @@ func (b *builderClient) run() (string, error) {
|
| 182 | 182 |
return "", err |
| 183 | 183 |
} |
| 184 | 184 |
|
| 185 |
- apiRun := &ApiRun{}
|
|
| 185 |
+ apiRun := &APIRun{}
|
|
| 186 | 186 |
if err := json.Unmarshal(body, apiRun); err != nil {
|
| 187 | 187 |
return "", err |
| 188 | 188 |
} |
| ... | ... |
@@ -191,18 +191,18 @@ func (b *builderClient) run() (string, error) {
|
| 191 | 191 |
} |
| 192 | 192 |
|
| 193 | 193 |
//start the container |
| 194 |
- _, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/start", nil)
|
|
| 194 |
+ _, _, err = b.cli.call("POST", "/containers/"+apiRun.ID+"/start", nil)
|
|
| 195 | 195 |
if err != nil {
|
| 196 | 196 |
return "", err |
| 197 | 197 |
} |
| 198 |
- b.tmpContainers[apiRun.Id] = struct{}{}
|
|
| 198 |
+ b.tmpContainers[apiRun.ID] = struct{}{}
|
|
| 199 | 199 |
|
| 200 | 200 |
// Wait for it to finish |
| 201 |
- body, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/wait", nil)
|
|
| 201 |
+ body, _, err = b.cli.call("POST", "/containers/"+apiRun.ID+"/wait", nil)
|
|
| 202 | 202 |
if err != nil {
|
| 203 | 203 |
return "", err |
| 204 | 204 |
} |
| 205 |
- apiWait := &ApiWait{}
|
|
| 205 |
+ apiWait := &APIWait{}
|
|
| 206 | 206 |
if err := json.Unmarshal(body, apiWait); err != nil {
|
| 207 | 207 |
return "", err |
| 208 | 208 |
} |
| ... | ... |
@@ -210,7 +210,7 @@ func (b *builderClient) run() (string, error) {
|
| 210 | 210 |
return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, apiWait.StatusCode)
|
| 211 | 211 |
} |
| 212 | 212 |
|
| 213 |
- return apiRun.Id, nil |
|
| 213 |
+ return apiRun.ID, nil |
|
| 214 | 214 |
} |
| 215 | 215 |
|
| 216 | 216 |
func (b *builderClient) commit(id string) error {
|
| ... | ... |
@@ -222,11 +222,11 @@ func (b *builderClient) commit(id string) error {
|
| 222 | 222 |
if id == "" {
|
| 223 | 223 |
cmd := b.config.Cmd |
| 224 | 224 |
b.config.Cmd = []string{"true"}
|
| 225 |
- if cid, err := b.run(); err != nil {
|
|
| 225 |
+ cid, err := b.run() |
|
| 226 |
+ if err != nil {
|
|
| 226 | 227 |
return err |
| 227 |
- } else {
|
|
| 228 |
- id = cid |
|
| 229 | 228 |
} |
| 229 |
+ id = cid |
|
| 230 | 230 |
b.config.Cmd = cmd |
| 231 | 231 |
} |
| 232 | 232 |
|
| ... | ... |
@@ -239,12 +239,12 @@ func (b *builderClient) commit(id string) error {
|
| 239 | 239 |
if err != nil {
|
| 240 | 240 |
return err |
| 241 | 241 |
} |
| 242 |
- apiId := &ApiId{}
|
|
| 243 |
- if err := json.Unmarshal(body, apiId); err != nil {
|
|
| 242 |
+ apiID := &APIID{}
|
|
| 243 |
+ if err := json.Unmarshal(body, apiID); err != nil {
|
|
| 244 | 244 |
return err |
| 245 | 245 |
} |
| 246 |
- b.tmpImages[apiId.Id] = struct{}{}
|
|
| 247 |
- b.image = apiId.Id |
|
| 246 |
+ b.tmpImages[apiID.ID] = struct{}{}
|
|
| 247 |
+ b.image = apiID.ID |
|
| 248 | 248 |
b.needCommit = false |
| 249 | 249 |
return nil |
| 250 | 250 |
} |
| ... | ... |
@@ -32,8 +32,6 @@ type buildFile struct {
|
| 32 | 32 |
tmpContainers map[string]struct{}
|
| 33 | 33 |
tmpImages map[string]struct{}
|
| 34 | 34 |
|
| 35 |
- needCommit bool |
|
| 36 |
- |
|
| 37 | 35 |
out io.Writer |
| 38 | 36 |
} |
| 39 | 37 |
|
| ... | ... |
@@ -63,7 +61,7 @@ func (b *buildFile) CmdFrom(name string) error {
|
| 63 | 63 |
remote = name |
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
- if err := b.srv.ImagePull(remote, tag, "", b.out, false); err != nil {
|
|
| 66 |
+ if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false)); err != nil {
|
|
| 67 | 67 |
return err |
| 68 | 68 |
} |
| 69 | 69 |
|
| ... | ... |
@@ -75,15 +73,14 @@ func (b *buildFile) CmdFrom(name string) error {
|
| 75 | 75 |
return err |
| 76 | 76 |
} |
| 77 | 77 |
} |
| 78 |
- b.image = image.Id |
|
| 78 |
+ b.image = image.ID |
|
| 79 | 79 |
b.config = &Config{}
|
| 80 | 80 |
return nil |
| 81 | 81 |
} |
| 82 | 82 |
|
| 83 | 83 |
func (b *buildFile) CmdMaintainer(name string) error {
|
| 84 |
- b.needCommit = true |
|
| 85 | 84 |
b.maintainer = name |
| 86 |
- return nil |
|
| 85 |
+ return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", name))
|
|
| 87 | 86 |
} |
| 88 | 87 |
|
| 89 | 88 |
func (b *buildFile) CmdRun(args string) error {
|
| ... | ... |
@@ -95,28 +92,34 @@ func (b *buildFile) CmdRun(args string) error {
|
| 95 | 95 |
return err |
| 96 | 96 |
} |
| 97 | 97 |
|
| 98 |
- cmd, env := b.config.Cmd, b.config.Env |
|
| 98 |
+ cmd := b.config.Cmd |
|
| 99 | 99 |
b.config.Cmd = nil |
| 100 | 100 |
MergeConfig(b.config, config) |
| 101 | 101 |
|
| 102 |
- if cache, err := b.srv.ImageGetCached(b.image, config); err != nil {
|
|
| 102 |
+ utils.Debugf("Command to be executed: %v", b.config.Cmd)
|
|
| 103 |
+ |
|
| 104 |
+ if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
|
| 103 | 105 |
return err |
| 104 | 106 |
} else if cache != nil {
|
| 105 |
- utils.Debugf("Use cached version")
|
|
| 106 |
- b.image = cache.Id |
|
| 107 |
+ utils.Debugf("[BUILDER] Use cached version")
|
|
| 108 |
+ b.image = cache.ID |
|
| 107 | 109 |
return nil |
| 110 |
+ } else {
|
|
| 111 |
+ utils.Debugf("[BUILDER] Cache miss")
|
|
| 108 | 112 |
} |
| 109 | 113 |
|
| 110 | 114 |
cid, err := b.run() |
| 111 | 115 |
if err != nil {
|
| 112 | 116 |
return err |
| 113 | 117 |
} |
| 114 |
- b.config.Cmd, b.config.Env = cmd, env |
|
| 115 |
- return b.commit(cid) |
|
| 118 |
+ if err := b.commit(cid, cmd, "run"); err != nil {
|
|
| 119 |
+ return err |
|
| 120 |
+ } |
|
| 121 |
+ b.config.Cmd = cmd |
|
| 122 |
+ return nil |
|
| 116 | 123 |
} |
| 117 | 124 |
|
| 118 | 125 |
func (b *buildFile) CmdEnv(args string) error {
|
| 119 |
- b.needCommit = true |
|
| 120 | 126 |
tmp := strings.SplitN(args, " ", 2) |
| 121 | 127 |
if len(tmp) != 2 {
|
| 122 | 128 |
return fmt.Errorf("Invalid ENV format")
|
| ... | ... |
@@ -131,60 +134,34 @@ func (b *buildFile) CmdEnv(args string) error {
|
| 131 | 131 |
} |
| 132 | 132 |
} |
| 133 | 133 |
b.config.Env = append(b.config.Env, key+"="+value) |
| 134 |
- return nil |
|
| 134 |
+ return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s=%s", key, value))
|
|
| 135 | 135 |
} |
| 136 | 136 |
|
| 137 | 137 |
func (b *buildFile) CmdCmd(args string) error {
|
| 138 |
- b.needCommit = true |
|
| 139 | 138 |
var cmd []string |
| 140 | 139 |
if err := json.Unmarshal([]byte(args), &cmd); err != nil {
|
| 141 | 140 |
utils.Debugf("Error unmarshalling: %s, using /bin/sh -c", err)
|
| 142 |
- b.config.Cmd = []string{"/bin/sh", "-c", args}
|
|
| 143 |
- } else {
|
|
| 144 |
- b.config.Cmd = cmd |
|
| 141 |
+ cmd = []string{"/bin/sh", "-c", args}
|
|
| 142 |
+ } |
|
| 143 |
+ if err := b.commit("", cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
|
|
| 144 |
+ return err |
|
| 145 | 145 |
} |
| 146 |
+ b.config.Cmd = cmd |
|
| 146 | 147 |
return nil |
| 147 | 148 |
} |
| 148 | 149 |
|
| 149 | 150 |
func (b *buildFile) CmdExpose(args string) error {
|
| 150 | 151 |
ports := strings.Split(args, " ") |
| 151 | 152 |
b.config.PortSpecs = append(ports, b.config.PortSpecs...) |
| 152 |
- return nil |
|
| 153 |
+ return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
|
|
| 153 | 154 |
} |
| 154 | 155 |
|
| 155 | 156 |
func (b *buildFile) CmdInsert(args string) error {
|
| 156 |
- if b.image == "" {
|
|
| 157 |
- return fmt.Errorf("Please provide a source image with `from` prior to insert")
|
|
| 158 |
- } |
|
| 159 |
- tmp := strings.SplitN(args, " ", 2) |
|
| 160 |
- if len(tmp) != 2 {
|
|
| 161 |
- return fmt.Errorf("Invalid INSERT format")
|
|
| 162 |
- } |
|
| 163 |
- sourceUrl := strings.Trim(tmp[0], " ") |
|
| 164 |
- destPath := strings.Trim(tmp[1], " ") |
|
| 165 |
- |
|
| 166 |
- file, err := utils.Download(sourceUrl, b.out) |
|
| 167 |
- if err != nil {
|
|
| 168 |
- return err |
|
| 169 |
- } |
|
| 170 |
- defer file.Body.Close() |
|
| 171 |
- |
|
| 172 |
- b.config.Cmd = []string{"echo", "INSERT", sourceUrl, "in", destPath}
|
|
| 173 |
- cid, err := b.run() |
|
| 174 |
- if err != nil {
|
|
| 175 |
- return err |
|
| 176 |
- } |
|
| 177 |
- |
|
| 178 |
- container := b.runtime.Get(cid) |
|
| 179 |
- if container == nil {
|
|
| 180 |
- return fmt.Errorf("An error occured while creating the container")
|
|
| 181 |
- } |
|
| 182 |
- |
|
| 183 |
- if err := container.Inject(file.Body, destPath); err != nil {
|
|
| 184 |
- return err |
|
| 185 |
- } |
|
| 157 |
+ return fmt.Errorf("INSERT has been deprecated. Please use ADD instead")
|
|
| 158 |
+} |
|
| 186 | 159 |
|
| 187 |
- return b.commit(cid) |
|
| 160 |
+func (b *buildFile) CmdCopy(args string) error {
|
|
| 161 |
+ return fmt.Errorf("COPY has been deprecated. Please use ADD instead")
|
|
| 188 | 162 |
} |
| 189 | 163 |
|
| 190 | 164 |
func (b *buildFile) CmdAdd(args string) error {
|
| ... | ... |
@@ -193,12 +170,13 @@ func (b *buildFile) CmdAdd(args string) error {
|
| 193 | 193 |
} |
| 194 | 194 |
tmp := strings.SplitN(args, " ", 2) |
| 195 | 195 |
if len(tmp) != 2 {
|
| 196 |
- return fmt.Errorf("Invalid INSERT format")
|
|
| 196 |
+ return fmt.Errorf("Invalid ADD format")
|
|
| 197 | 197 |
} |
| 198 | 198 |
orig := strings.Trim(tmp[0], " ") |
| 199 | 199 |
dest := strings.Trim(tmp[1], " ") |
| 200 | 200 |
|
| 201 |
- b.config.Cmd = []string{"echo", "PUSH", orig, "in", dest}
|
|
| 201 |
+ cmd := b.config.Cmd |
|
| 202 |
+ b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)}
|
|
| 202 | 203 |
cid, err := b.run() |
| 203 | 204 |
if err != nil {
|
| 204 | 205 |
return err |
| ... | ... |
@@ -208,19 +186,23 @@ func (b *buildFile) CmdAdd(args string) error {
|
| 208 | 208 |
if container == nil {
|
| 209 | 209 |
return fmt.Errorf("Error while creating the container (CmdAdd)")
|
| 210 | 210 |
} |
| 211 |
- |
|
| 212 |
- if err := os.MkdirAll(path.Join(container.rwPath(), dest), 0700); err != nil {
|
|
| 211 |
+ if err := container.EnsureMounted(); err != nil {
|
|
| 213 | 212 |
return err |
| 214 | 213 |
} |
| 214 |
+ defer container.Unmount() |
|
| 215 | 215 |
|
| 216 | 216 |
origPath := path.Join(b.context, orig) |
| 217 |
- destPath := path.Join(container.rwPath(), dest) |
|
| 217 |
+ destPath := path.Join(container.RootfsPath(), dest) |
|
| 218 | 218 |
|
| 219 | 219 |
fi, err := os.Stat(origPath) |
| 220 | 220 |
if err != nil {
|
| 221 | 221 |
return err |
| 222 | 222 |
} |
| 223 | 223 |
if fi.IsDir() {
|
| 224 |
+ if err := os.MkdirAll(destPath, 0700); err != nil {
|
|
| 225 |
+ return err |
|
| 226 |
+ } |
|
| 227 |
+ |
|
| 224 | 228 |
files, err := ioutil.ReadDir(path.Join(b.context, orig)) |
| 225 | 229 |
if err != nil {
|
| 226 | 230 |
return err |
| ... | ... |
@@ -231,12 +213,18 @@ func (b *buildFile) CmdAdd(args string) error {
|
| 231 | 231 |
} |
| 232 | 232 |
} |
| 233 | 233 |
} else {
|
| 234 |
+ if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil {
|
|
| 235 |
+ return err |
|
| 236 |
+ } |
|
| 234 | 237 |
if err := utils.CopyDirectory(origPath, destPath); err != nil {
|
| 235 | 238 |
return err |
| 236 | 239 |
} |
| 237 | 240 |
} |
| 238 |
- |
|
| 239 |
- return b.commit(cid) |
|
| 241 |
+ if err := b.commit(cid, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil {
|
|
| 242 |
+ return err |
|
| 243 |
+ } |
|
| 244 |
+ b.config.Cmd = cmd |
|
| 245 |
+ return nil |
|
| 240 | 246 |
} |
| 241 | 247 |
|
| 242 | 248 |
func (b *buildFile) run() (string, error) {
|
| ... | ... |
@@ -250,7 +238,7 @@ func (b *buildFile) run() (string, error) {
|
| 250 | 250 |
if err != nil {
|
| 251 | 251 |
return "", err |
| 252 | 252 |
} |
| 253 |
- b.tmpContainers[c.Id] = struct{}{}
|
|
| 253 |
+ b.tmpContainers[c.ID] = struct{}{}
|
|
| 254 | 254 |
|
| 255 | 255 |
//start the container |
| 256 | 256 |
if err := c.Start(); err != nil {
|
| ... | ... |
@@ -262,23 +250,33 @@ func (b *buildFile) run() (string, error) {
|
| 262 | 262 |
return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret)
|
| 263 | 263 |
} |
| 264 | 264 |
|
| 265 |
- return c.Id, nil |
|
| 265 |
+ return c.ID, nil |
|
| 266 | 266 |
} |
| 267 | 267 |
|
| 268 |
-func (b *buildFile) commit(id string) error {
|
|
| 268 |
+// Commit the container <id> with the autorun command <autoCmd> |
|
| 269 |
+func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
|
| 269 | 270 |
if b.image == "" {
|
| 270 | 271 |
return fmt.Errorf("Please provide a source image with `from` prior to commit")
|
| 271 | 272 |
} |
| 272 | 273 |
b.config.Image = b.image |
| 273 | 274 |
if id == "" {
|
| 274 |
- cmd := b.config.Cmd |
|
| 275 |
- b.config.Cmd = []string{"true"}
|
|
| 276 |
- if cid, err := b.run(); err != nil {
|
|
| 275 |
+ b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
|
|
| 276 |
+ |
|
| 277 |
+ if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
|
| 277 | 278 |
return err |
| 279 |
+ } else if cache != nil {
|
|
| 280 |
+ utils.Debugf("[BUILDER] Use cached version")
|
|
| 281 |
+ b.image = cache.ID |
|
| 282 |
+ return nil |
|
| 278 | 283 |
} else {
|
| 279 |
- id = cid |
|
| 284 |
+ utils.Debugf("[BUILDER] Cache miss")
|
|
| 285 |
+ } |
|
| 286 |
+ |
|
| 287 |
+ cid, err := b.run() |
|
| 288 |
+ if err != nil {
|
|
| 289 |
+ return err |
|
| 280 | 290 |
} |
| 281 |
- b.config.Cmd = cmd |
|
| 291 |
+ id = cid |
|
| 282 | 292 |
} |
| 283 | 293 |
|
| 284 | 294 |
container := b.runtime.Get(id) |
| ... | ... |
@@ -286,20 +284,20 @@ func (b *buildFile) commit(id string) error {
|
| 286 | 286 |
return fmt.Errorf("An error occured while creating the container")
|
| 287 | 287 |
} |
| 288 | 288 |
|
| 289 |
+ // Note: Actually copy the struct |
|
| 290 |
+ autoConfig := *b.config |
|
| 291 |
+ autoConfig.Cmd = autoCmd |
|
| 289 | 292 |
// Commit the container |
| 290 |
- image, err := b.builder.Commit(container, "", "", "", b.maintainer, nil) |
|
| 293 |
+ image, err := b.builder.Commit(container, "", "", "", b.maintainer, &autoConfig) |
|
| 291 | 294 |
if err != nil {
|
| 292 | 295 |
return err |
| 293 | 296 |
} |
| 294 |
- b.tmpImages[image.Id] = struct{}{}
|
|
| 295 |
- b.image = image.Id |
|
| 296 |
- b.needCommit = false |
|
| 297 |
+ b.tmpImages[image.ID] = struct{}{}
|
|
| 298 |
+ b.image = image.ID |
|
| 297 | 299 |
return nil |
| 298 | 300 |
} |
| 299 | 301 |
|
| 300 | 302 |
func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
|
| 301 |
- defer b.clearTmp(b.tmpContainers, b.tmpImages) |
|
| 302 |
- |
|
| 303 | 303 |
if context != nil {
|
| 304 | 304 |
name, err := ioutil.TempDir("/tmp", "docker-build")
|
| 305 | 305 |
if err != nil {
|
| ... | ... |
@@ -337,6 +335,7 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
|
| 337 | 337 |
method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
|
| 338 | 338 |
if !exists {
|
| 339 | 339 |
fmt.Fprintf(b.out, "Skipping unknown instruction %s\n", strings.ToUpper(instruction)) |
| 340 |
+ continue |
|
| 340 | 341 |
} |
| 341 | 342 |
ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
|
| 342 | 343 |
if ret != nil {
|
| ... | ... |
@@ -345,22 +344,10 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
|
| 345 | 345 |
|
| 346 | 346 |
fmt.Fprintf(b.out, "===> %v\n", b.image) |
| 347 | 347 |
} |
| 348 |
- if b.needCommit {
|
|
| 349 |
- if err := b.commit(""); err != nil {
|
|
| 350 |
- return "", err |
|
| 351 |
- } |
|
| 352 |
- } |
|
| 353 | 348 |
if b.image != "" {
|
| 354 |
- // The build is successful, keep the temporary containers and images |
|
| 355 |
- for i := range b.tmpImages {
|
|
| 356 |
- delete(b.tmpImages, i) |
|
| 357 |
- } |
|
| 358 |
- fmt.Fprintf(b.out, "Build success.\n Image id:\n%s\n", b.image) |
|
| 349 |
+ fmt.Fprintf(b.out, "Build successful.\n===> %s\n", b.image) |
|
| 359 | 350 |
return b.image, nil |
| 360 | 351 |
} |
| 361 |
- for i := range b.tmpContainers {
|
|
| 362 |
- delete(b.tmpContainers, i) |
|
| 363 |
- } |
|
| 364 | 352 |
return "", fmt.Errorf("An error occured during the build\n")
|
| 365 | 353 |
} |
| 366 | 354 |
|
| ... | ... |
@@ -26,7 +26,7 @@ func TestBuild(t *testing.T) {
|
| 26 | 26 |
|
| 27 | 27 |
buildfile := NewBuildFile(srv, &utils.NopWriter{})
|
| 28 | 28 |
|
| 29 |
- imgId, err := buildfile.Build(strings.NewReader(Dockerfile), nil) |
|
| 29 |
+ imgID, err := buildfile.Build(strings.NewReader(Dockerfile), nil) |
|
| 30 | 30 |
if err != nil {
|
| 31 | 31 |
t.Fatal(err) |
| 32 | 32 |
} |
| ... | ... |
@@ -34,7 +34,7 @@ func TestBuild(t *testing.T) {
|
| 34 | 34 |
builder := NewBuilder(runtime) |
| 35 | 35 |
container, err := builder.Create( |
| 36 | 36 |
&Config{
|
| 37 |
- Image: imgId, |
|
| 37 |
+ Image: imgID, |
|
| 38 | 38 |
Cmd: []string{"cat", "/tmp/passwd"},
|
| 39 | 39 |
}, |
| 40 | 40 |
) |
| ... | ... |
@@ -53,7 +53,7 @@ func TestBuild(t *testing.T) {
|
| 53 | 53 |
|
| 54 | 54 |
container2, err := builder.Create( |
| 55 | 55 |
&Config{
|
| 56 |
- Image: imgId, |
|
| 56 |
+ Image: imgID, |
|
| 57 | 57 |
Cmd: []string{"ls", "-d", "/var/run/sshd"},
|
| 58 | 58 |
}, |
| 59 | 59 |
) |
| ... | ... |
@@ -65,7 +65,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
|
| 65 | 65 |
file := filepath.Base(path) |
| 66 | 66 |
// If there is a whiteout, then the file was removed |
| 67 | 67 |
if strings.HasPrefix(file, ".wh.") {
|
| 68 |
- originalFile := strings.TrimLeft(file, ".wh.") |
|
| 68 |
+ originalFile := file[len(".wh."):]
|
|
| 69 | 69 |
change.Path = filepath.Join(filepath.Dir(path), originalFile) |
| 70 | 70 |
change.Kind = ChangeDelete |
| 71 | 71 |
} else {
|
| ... | ... |
@@ -17,6 +17,7 @@ import ( |
| 17 | 17 |
"net/url" |
| 18 | 18 |
"os" |
| 19 | 19 |
"os/signal" |
| 20 |
+ "path" |
|
| 20 | 21 |
"path/filepath" |
| 21 | 22 |
"reflect" |
| 22 | 23 |
"strconv" |
| ... | ... |
@@ -27,10 +28,10 @@ import ( |
| 27 | 27 |
"unicode" |
| 28 | 28 |
) |
| 29 | 29 |
|
| 30 |
-const VERSION = "0.3.3" |
|
| 30 |
+const VERSION = "0.4.0" |
|
| 31 | 31 |
|
| 32 | 32 |
var ( |
| 33 |
- GIT_COMMIT string |
|
| 33 |
+ GITCOMMIT string |
|
| 34 | 34 |
) |
| 35 | 35 |
|
| 36 | 36 |
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
|
| ... | ... |
@@ -101,7 +102,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
| 101 | 101 |
{"stop", "Stop a running container"},
|
| 102 | 102 |
{"tag", "Tag an image into a repository"},
|
| 103 | 103 |
{"version", "Show the docker version information"},
|
| 104 |
- {"wait", "Block until a container stops}, then print its exit code"},
|
|
| 104 |
+ {"wait", "Block until a container stops, then print its exit code"},
|
|
| 105 | 105 |
} {
|
| 106 | 106 |
help += fmt.Sprintf(" %-10.10s%s\n", command[0], command[1])
|
| 107 | 107 |
} |
| ... | ... |
@@ -130,16 +131,20 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
|
| 130 | 130 |
} |
| 131 | 131 |
|
| 132 | 132 |
func (cli *DockerCli) CmdBuild(args ...string) error {
|
| 133 |
- cmd := Subcmd("build", "[OPTIONS] [CONTEXT]", "Build an image from a Dockerfile")
|
|
| 134 |
- fileName := cmd.String("f", "Dockerfile", "Use `file` as Dockerfile. Can be '-' for stdin")
|
|
| 133 |
+ cmd := Subcmd("build", "[OPTIONS] PATH | -", "Build a new container image from the source code at PATH")
|
|
| 134 |
+ tag := cmd.String("t", "", "Tag to be applied to the resulting image in case of success")
|
|
| 135 | 135 |
if err := cmd.Parse(args); err != nil {
|
| 136 | 136 |
return nil |
| 137 | 137 |
} |
| 138 |
+ if cmd.NArg() != 1 {
|
|
| 139 |
+ cmd.Usage() |
|
| 140 |
+ return nil |
|
| 141 |
+ } |
|
| 138 | 142 |
|
| 139 | 143 |
var ( |
| 140 |
- file io.ReadCloser |
|
| 141 | 144 |
multipartBody io.Reader |
| 142 |
- err error |
|
| 145 |
+ file io.ReadCloser |
|
| 146 |
+ contextPath string |
|
| 143 | 147 |
) |
| 144 | 148 |
|
| 145 | 149 |
// Init the needed component for the Multipart |
| ... | ... |
@@ -148,27 +153,19 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
| 148 | 148 |
w := multipart.NewWriter(buff) |
| 149 | 149 |
boundary := strings.NewReader("\r\n--" + w.Boundary() + "--\r\n")
|
| 150 | 150 |
|
| 151 |
- // Create a FormFile multipart for the Dockerfile |
|
| 152 |
- if *fileName == "-" {
|
|
| 151 |
+ compression := Bzip2 |
|
| 152 |
+ |
|
| 153 |
+ if cmd.Arg(0) == "-" {
|
|
| 153 | 154 |
file = os.Stdin |
| 154 | 155 |
} else {
|
| 155 |
- file, err = os.Open(*fileName) |
|
| 156 |
+ // Send Dockerfile from arg/Dockerfile (deprecate later) |
|
| 157 |
+ f, err := os.Open(path.Join(cmd.Arg(0), "Dockerfile")) |
|
| 156 | 158 |
if err != nil {
|
| 157 | 159 |
return err |
| 158 | 160 |
} |
| 159 |
- defer file.Close() |
|
| 160 |
- } |
|
| 161 |
- if wField, err := w.CreateFormFile("Dockerfile", *fileName); err != nil {
|
|
| 162 |
- return err |
|
| 163 |
- } else {
|
|
| 164 |
- io.Copy(wField, file) |
|
| 165 |
- } |
|
| 166 |
- multipartBody = io.MultiReader(multipartBody, boundary) |
|
| 167 |
- |
|
| 168 |
- compression := Bzip2 |
|
| 169 |
- |
|
| 170 |
- // Create a FormFile multipart for the context if needed |
|
| 171 |
- if cmd.Arg(0) != "" {
|
|
| 161 |
+ file = f |
|
| 162 |
+ // Send context from arg |
|
| 163 |
+ // Create a FormFile multipart for the context if needed |
|
| 172 | 164 |
// FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage? |
| 173 | 165 |
context, err := Tar(cmd.Arg(0), compression) |
| 174 | 166 |
if err != nil {
|
| ... | ... |
@@ -179,23 +176,32 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
| 179 | 179 |
if err != nil {
|
| 180 | 180 |
return err |
| 181 | 181 |
} |
| 182 |
- if wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension()); err != nil {
|
|
| 182 |
+ wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension())
|
|
| 183 |
+ if err != nil {
|
|
| 183 | 184 |
return err |
| 184 |
- } else {
|
|
| 185 |
- // FIXME: Find a way to have a progressbar for the upload too |
|
| 186 |
- io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, "Caching Context %v/%v (%v)\r", false)) |
|
| 187 | 185 |
} |
| 188 |
- |
|
| 186 |
+ // FIXME: Find a way to have a progressbar for the upload too |
|
| 187 |
+ sf := utils.NewStreamFormatter(false) |
|
| 188 |
+ io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, sf.FormatProgress("Caching Context", "%v/%v (%v)"), sf))
|
|
| 189 | 189 |
multipartBody = io.MultiReader(multipartBody, boundary) |
| 190 | 190 |
} |
| 191 |
+ // Create a FormFile multipart for the Dockerfile |
|
| 192 |
+ wField, err := w.CreateFormFile("Dockerfile", "Dockerfile")
|
|
| 193 |
+ if err != nil {
|
|
| 194 |
+ return err |
|
| 195 |
+ } |
|
| 196 |
+ io.Copy(wField, file) |
|
| 197 |
+ multipartBody = io.MultiReader(multipartBody, boundary) |
|
| 191 | 198 |
|
| 199 |
+ v := &url.Values{}
|
|
| 200 |
+ v.Set("t", *tag)
|
|
| 192 | 201 |
// Send the multipart request with correct content-type |
| 193 |
- req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, "/build"), multipartBody)
|
|
| 202 |
+ req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s?%s", cli.host, cli.port, "/build", v.Encode()), multipartBody)
|
|
| 194 | 203 |
if err != nil {
|
| 195 | 204 |
return err |
| 196 | 205 |
} |
| 197 | 206 |
req.Header.Set("Content-Type", w.FormDataContentType())
|
| 198 |
- if cmd.Arg(0) != "" {
|
|
| 207 |
+ if contextPath != "" {
|
|
| 199 | 208 |
req.Header.Set("X-Docker-Context-Compression", compression.Flag())
|
| 200 | 209 |
fmt.Println("Uploading Context...")
|
| 201 | 210 |
} |
| ... | ... |
@@ -270,9 +276,8 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
| 270 | 270 |
oldState, err := term.SetRawTerminal() |
| 271 | 271 |
if err != nil {
|
| 272 | 272 |
return err |
| 273 |
- } else {
|
|
| 274 |
- defer term.RestoreTerminal(oldState) |
|
| 275 | 273 |
} |
| 274 |
+ defer term.RestoreTerminal(oldState) |
|
| 276 | 275 |
|
| 277 | 276 |
cmd := Subcmd("login", "", "Register or Login to the docker registry server")
|
| 278 | 277 |
if err := cmd.Parse(args); err != nil {
|
| ... | ... |
@@ -325,7 +330,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
| 325 | 325 |
return err |
| 326 | 326 |
} |
| 327 | 327 |
|
| 328 |
- var out2 ApiAuth |
|
| 328 |
+ var out2 APIAuth |
|
| 329 | 329 |
err = json.Unmarshal(body, &out2) |
| 330 | 330 |
if err != nil {
|
| 331 | 331 |
return err |
| ... | ... |
@@ -352,7 +357,7 @@ func (cli *DockerCli) CmdWait(args ...string) error {
|
| 352 | 352 |
if err != nil {
|
| 353 | 353 |
fmt.Printf("%s", err)
|
| 354 | 354 |
} else {
|
| 355 |
- var out ApiWait |
|
| 355 |
+ var out APIWait |
|
| 356 | 356 |
err = json.Unmarshal(body, &out) |
| 357 | 357 |
if err != nil {
|
| 358 | 358 |
return err |
| ... | ... |
@@ -380,21 +385,20 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
|
| 380 | 380 |
return err |
| 381 | 381 |
} |
| 382 | 382 |
|
| 383 |
- var out ApiVersion |
|
| 383 |
+ var out APIVersion |
|
| 384 | 384 |
err = json.Unmarshal(body, &out) |
| 385 | 385 |
if err != nil {
|
| 386 | 386 |
utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
|
| 387 | 387 |
return err |
| 388 | 388 |
} |
| 389 |
- fmt.Println("Version:", out.Version)
|
|
| 390 |
- fmt.Println("Git Commit:", out.GitCommit)
|
|
| 391 |
- if !out.MemoryLimit {
|
|
| 392 |
- fmt.Println("WARNING: No memory limit support")
|
|
| 389 |
+ fmt.Println("Client version:", VERSION)
|
|
| 390 |
+ fmt.Println("Server version:", out.Version)
|
|
| 391 |
+ if out.GitCommit != "" {
|
|
| 392 |
+ fmt.Println("Git commit:", out.GitCommit)
|
|
| 393 | 393 |
} |
| 394 |
- if !out.SwapLimit {
|
|
| 395 |
- fmt.Println("WARNING: No swap limit support")
|
|
| 394 |
+ if out.GoVersion != "" {
|
|
| 395 |
+ fmt.Println("Go version:", out.GoVersion)
|
|
| 396 | 396 |
} |
| 397 |
- |
|
| 398 | 397 |
return nil |
| 399 | 398 |
} |
| 400 | 399 |
|
| ... | ... |
@@ -414,15 +418,24 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
| 414 | 414 |
return err |
| 415 | 415 |
} |
| 416 | 416 |
|
| 417 |
- var out ApiInfo |
|
| 418 |
- err = json.Unmarshal(body, &out) |
|
| 419 |
- if err != nil {
|
|
| 417 |
+ var out APIInfo |
|
| 418 |
+ if err := json.Unmarshal(body, &out); err != nil {
|
|
| 420 | 419 |
return err |
| 421 | 420 |
} |
| 422 |
- fmt.Printf("containers: %d\nversion: %s\nimages: %d\nGo version: %s\n", out.Containers, out.Version, out.Images, out.GoVersion)
|
|
| 423 |
- if out.Debug {
|
|
| 424 |
- fmt.Println("debug mode enabled")
|
|
| 425 |
- fmt.Printf("fds: %d\ngoroutines: %d\n", out.NFd, out.NGoroutines)
|
|
| 421 |
+ |
|
| 422 |
+ fmt.Printf("Containers: %d\n", out.Containers)
|
|
| 423 |
+ fmt.Printf("Images: %d\n", out.Images)
|
|
| 424 |
+ if out.Debug || os.Getenv("DEBUG") != "" {
|
|
| 425 |
+ fmt.Printf("Debug mode (server): %v\n", out.Debug)
|
|
| 426 |
+ fmt.Printf("Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
|
|
| 427 |
+ fmt.Printf("Fds: %d\n", out.NFd)
|
|
| 428 |
+ fmt.Printf("Goroutines: %d\n", out.NGoroutines)
|
|
| 429 |
+ } |
|
| 430 |
+ if !out.MemoryLimit {
|
|
| 431 |
+ fmt.Println("WARNING: No memory limit support")
|
|
| 432 |
+ } |
|
| 433 |
+ if !out.SwapLimit {
|
|
| 434 |
+ fmt.Println("WARNING: No swap limit support")
|
|
| 426 | 435 |
} |
| 427 | 436 |
return nil |
| 428 | 437 |
} |
| ... | ... |
@@ -590,7 +603,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
|
| 590 | 590 |
return err |
| 591 | 591 |
} |
| 592 | 592 |
|
| 593 |
- var outs []ApiHistory |
|
| 593 |
+ var outs []APIHistory |
|
| 594 | 594 |
err = json.Unmarshal(body, &outs) |
| 595 | 595 |
if err != nil {
|
| 596 | 596 |
return err |
| ... | ... |
@@ -599,7 +612,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
|
| 599 | 599 |
fmt.Fprintln(w, "ID\tCREATED\tCREATED BY") |
| 600 | 600 |
|
| 601 | 601 |
for _, out := range outs {
|
| 602 |
- fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.Id, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy) |
|
| 602 |
+ fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy) |
|
| 603 | 603 |
} |
| 604 | 604 |
w.Flush() |
| 605 | 605 |
return nil |
| ... | ... |
@@ -725,12 +738,6 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
| 725 | 725 |
remote = remoteParts[0] |
| 726 | 726 |
} |
| 727 | 727 |
|
| 728 |
- if strings.Contains(remote, "/") {
|
|
| 729 |
- if _, err := cli.checkIfLogged(true, "pull"); err != nil {
|
|
| 730 |
- return err |
|
| 731 |
- } |
|
| 732 |
- } |
|
| 733 |
- |
|
| 734 | 728 |
v := url.Values{}
|
| 735 | 729 |
v.Set("fromImage", remote)
|
| 736 | 730 |
v.Set("tag", *tag)
|
| ... | ... |
@@ -778,7 +785,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
| 778 | 778 |
return err |
| 779 | 779 |
} |
| 780 | 780 |
|
| 781 |
- var outs []ApiImages |
|
| 781 |
+ var outs []APIImages |
|
| 782 | 782 |
err = json.Unmarshal(body, &outs) |
| 783 | 783 |
if err != nil {
|
| 784 | 784 |
return err |
| ... | ... |
@@ -800,9 +807,9 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
| 800 | 800 |
if !*quiet {
|
| 801 | 801 |
fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag) |
| 802 | 802 |
if *noTrunc {
|
| 803 |
- fmt.Fprintf(w, "%s\t", out.Id) |
|
| 803 |
+ fmt.Fprintf(w, "%s\t", out.ID) |
|
| 804 | 804 |
} else {
|
| 805 |
- fmt.Fprintf(w, "%s\t", utils.TruncateId(out.Id)) |
|
| 805 |
+ fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID)) |
|
| 806 | 806 |
} |
| 807 | 807 |
fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) |
| 808 | 808 |
if out.ParentSize > 0 {
|
| ... | ... |
@@ -812,9 +819,9 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
| 812 | 812 |
} |
| 813 | 813 |
} else {
|
| 814 | 814 |
if *noTrunc {
|
| 815 |
- fmt.Fprintln(w, out.Id) |
|
| 815 |
+ fmt.Fprintln(w, out.ID) |
|
| 816 | 816 |
} else {
|
| 817 |
- fmt.Fprintln(w, utils.TruncateId(out.Id)) |
|
| 817 |
+ fmt.Fprintln(w, utils.TruncateID(out.ID)) |
|
| 818 | 818 |
} |
| 819 | 819 |
} |
| 820 | 820 |
} |
| ... | ... |
@@ -861,7 +868,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
| 861 | 861 |
return err |
| 862 | 862 |
} |
| 863 | 863 |
|
| 864 |
- var outs []ApiContainers |
|
| 864 |
+ var outs []APIContainers |
|
| 865 | 865 |
err = json.Unmarshal(body, &outs) |
| 866 | 866 |
if err != nil {
|
| 867 | 867 |
return err |
| ... | ... |
@@ -874,9 +881,9 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
| 874 | 874 |
for _, out := range outs {
|
| 875 | 875 |
if !*quiet {
|
| 876 | 876 |
if *noTrunc {
|
| 877 |
- fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.Id, out.Image, out.Command, out.Status, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports) |
|
| 877 |
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) |
|
| 878 | 878 |
} else {
|
| 879 |
- fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateId(out.Id), out.Image, utils.Trunc(out.Command, 20), out.Status, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports) |
|
| 879 |
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) |
|
| 880 | 880 |
} |
| 881 | 881 |
if out.SizeRootFs > 0 {
|
| 882 | 882 |
fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs)) |
| ... | ... |
@@ -885,9 +892,9 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
| 885 | 885 |
} |
| 886 | 886 |
} else {
|
| 887 | 887 |
if *noTrunc {
|
| 888 |
- fmt.Fprintln(w, out.Id) |
|
| 888 |
+ fmt.Fprintln(w, out.ID) |
|
| 889 | 889 |
} else {
|
| 890 |
- fmt.Fprintln(w, utils.TruncateId(out.Id)) |
|
| 890 |
+ fmt.Fprintln(w, utils.TruncateID(out.ID)) |
|
| 891 | 891 |
} |
| 892 | 892 |
} |
| 893 | 893 |
} |
| ... | ... |
@@ -930,13 +937,13 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
|
| 930 | 930 |
return err |
| 931 | 931 |
} |
| 932 | 932 |
|
| 933 |
- apiId := &ApiId{}
|
|
| 934 |
- err = json.Unmarshal(body, apiId) |
|
| 933 |
+ apiID := &APIID{}
|
|
| 934 |
+ err = json.Unmarshal(body, apiID) |
|
| 935 | 935 |
if err != nil {
|
| 936 | 936 |
return err |
| 937 | 937 |
} |
| 938 | 938 |
|
| 939 |
- fmt.Println(apiId.Id) |
|
| 939 |
+ fmt.Println(apiID.ID) |
|
| 940 | 940 |
return nil |
| 941 | 941 |
} |
| 942 | 942 |
|
| ... | ... |
@@ -993,12 +1000,10 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
| 993 | 993 |
return nil |
| 994 | 994 |
} |
| 995 | 995 |
|
| 996 |
- v := url.Values{}
|
|
| 997 |
- v.Set("logs", "1")
|
|
| 998 |
- v.Set("stdout", "1")
|
|
| 999 |
- v.Set("stderr", "1")
|
|
| 1000 |
- |
|
| 1001 |
- if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), false, nil, os.Stdout); err != nil {
|
|
| 996 |
+ if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", nil, os.Stdout); err != nil {
|
|
| 997 |
+ return err |
|
| 998 |
+ } |
|
| 999 |
+ if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", nil, os.Stderr); err != nil {
|
|
| 1002 | 1000 |
return err |
| 1003 | 1001 |
} |
| 1004 | 1002 |
return nil |
| ... | ... |
@@ -1075,7 +1080,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
|
| 1075 | 1075 |
return err |
| 1076 | 1076 |
} |
| 1077 | 1077 |
|
| 1078 |
- outs := []ApiSearch{}
|
|
| 1078 |
+ outs := []APISearch{}
|
|
| 1079 | 1079 |
err = json.Unmarshal(body, &outs) |
| 1080 | 1080 |
if err != nil {
|
| 1081 | 1081 |
return err |
| ... | ... |
@@ -1207,7 +1212,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 1207 | 1207 |
return err |
| 1208 | 1208 |
} |
| 1209 | 1209 |
|
| 1210 |
- out := &ApiRun{}
|
|
| 1210 |
+ out := &APIRun{}
|
|
| 1211 | 1211 |
err = json.Unmarshal(body, out) |
| 1212 | 1212 |
if err != nil {
|
| 1213 | 1213 |
return err |
| ... | ... |
@@ -1228,18 +1233,21 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 1228 | 1228 |
} |
| 1229 | 1229 |
|
| 1230 | 1230 |
//start the container |
| 1231 |
- _, _, err = cli.call("POST", "/containers/"+out.Id+"/start", nil)
|
|
| 1231 |
+ _, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil)
|
|
| 1232 | 1232 |
if err != nil {
|
| 1233 | 1233 |
return err |
| 1234 | 1234 |
} |
| 1235 | 1235 |
|
| 1236 |
+ if !config.AttachStdout && !config.AttachStderr {
|
|
| 1237 |
+ fmt.Println(out.ID) |
|
| 1238 |
+ } |
|
| 1236 | 1239 |
if connections > 0 {
|
| 1237 | 1240 |
chErrors := make(chan error, connections) |
| 1238 |
- cli.monitorTtySize(out.Id) |
|
| 1241 |
+ cli.monitorTtySize(out.ID) |
|
| 1239 | 1242 |
|
| 1240 | 1243 |
if splitStderr && config.AttachStderr {
|
| 1241 | 1244 |
go func() {
|
| 1242 |
- chErrors <- cli.hijack("POST", "/containers/"+out.Id+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr)
|
|
| 1245 |
+ chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr)
|
|
| 1243 | 1246 |
}() |
| 1244 | 1247 |
} |
| 1245 | 1248 |
|
| ... | ... |
@@ -1257,7 +1265,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 1257 | 1257 |
v.Set("stderr", "1")
|
| 1258 | 1258 |
} |
| 1259 | 1259 |
go func() {
|
| 1260 |
- chErrors <- cli.hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout)
|
|
| 1260 |
+ chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout)
|
|
| 1261 | 1261 |
}() |
| 1262 | 1262 |
for connections > 0 {
|
| 1263 | 1263 |
err := <-chErrors |
| ... | ... |
@@ -1267,9 +1275,6 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 1267 | 1267 |
connections -= 1 |
| 1268 | 1268 |
} |
| 1269 | 1269 |
} |
| 1270 |
- if !config.AttachStdout && !config.AttachStderr {
|
|
| 1271 |
- fmt.Println(out.Id) |
|
| 1272 |
- } |
|
| 1273 | 1270 |
return nil |
| 1274 | 1271 |
} |
| 1275 | 1272 |
|
| ... | ... |
@@ -1317,7 +1322,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
|
| 1317 | 1317 |
params = bytes.NewBuffer(buf) |
| 1318 | 1318 |
} |
| 1319 | 1319 |
|
| 1320 |
- req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), params)
|
|
| 1320 |
+ req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), params)
|
|
| 1321 | 1321 |
if err != nil {
|
| 1322 | 1322 |
return nil, -1, err |
| 1323 | 1323 |
} |
| ... | ... |
@@ -1349,7 +1354,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e |
| 1349 | 1349 |
if (method == "POST" || method == "PUT") && in == nil {
|
| 1350 | 1350 |
in = bytes.NewReader([]byte{})
|
| 1351 | 1351 |
} |
| 1352 |
- req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), in)
|
|
| 1352 |
+ req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), in)
|
|
| 1353 | 1353 |
if err != nil {
|
| 1354 | 1354 |
return err |
| 1355 | 1355 |
} |
| ... | ... |
@@ -1374,20 +1379,18 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e |
| 1374 | 1374 |
} |
| 1375 | 1375 |
|
| 1376 | 1376 |
if resp.Header.Get("Content-Type") == "application/json" {
|
| 1377 |
- type Message struct {
|
|
| 1378 |
- Status string `json:"status,omitempty"` |
|
| 1379 |
- Progress string `json:"progress,omitempty"` |
|
| 1380 |
- } |
|
| 1381 | 1377 |
dec := json.NewDecoder(resp.Body) |
| 1382 | 1378 |
for {
|
| 1383 |
- var m Message |
|
| 1379 |
+ var m utils.JSONMessage |
|
| 1384 | 1380 |
if err := dec.Decode(&m); err == io.EOF {
|
| 1385 | 1381 |
break |
| 1386 | 1382 |
} else if err != nil {
|
| 1387 | 1383 |
return err |
| 1388 | 1384 |
} |
| 1389 | 1385 |
if m.Progress != "" {
|
| 1390 |
- fmt.Fprintf(out, "Downloading %s\r", m.Progress) |
|
| 1386 |
+ fmt.Fprintf(out, "%s %s\r", m.Status, m.Progress) |
|
| 1387 |
+ } else if m.Error != "" {
|
|
| 1388 |
+ return fmt.Errorf(m.Error) |
|
| 1391 | 1389 |
} else {
|
| 1392 | 1390 |
fmt.Fprintf(out, "%s\n", m.Status) |
| 1393 | 1391 |
} |
| ... | ... |
@@ -1401,7 +1404,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e |
| 1401 | 1401 |
} |
| 1402 | 1402 |
|
| 1403 | 1403 |
func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.File, out io.Writer) error {
|
| 1404 |
- req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", API_VERSION, path), nil)
|
|
| 1404 |
+ req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
|
|
| 1405 | 1405 |
if err != nil {
|
| 1406 | 1406 |
return err |
| 1407 | 1407 |
} |
| ... | ... |
@@ -1422,12 +1425,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi |
| 1422 | 1422 |
return err |
| 1423 | 1423 |
}) |
| 1424 | 1424 |
|
| 1425 |
- if in != nil && setRawTerminal && term.IsTerminal(int(in.Fd())) && os.Getenv("NORAW") == "" {
|
|
| 1426 |
- if oldState, err := term.SetRawTerminal(); err != nil {
|
|
| 1425 |
+ if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" {
|
|
| 1426 |
+ oldState, err := term.SetRawTerminal() |
|
| 1427 |
+ if err != nil {
|
|
| 1427 | 1428 |
return err |
| 1428 |
- } else {
|
|
| 1429 |
- defer term.RestoreTerminal(oldState) |
|
| 1430 | 1429 |
} |
| 1430 |
+ defer term.RestoreTerminal(oldState) |
|
| 1431 | 1431 |
} |
| 1432 | 1432 |
sendStdin := utils.Go(func() error {
|
| 1433 | 1433 |
_, err := io.Copy(rwc, in) |
| ... | ... |
@@ -1441,7 +1444,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi |
| 1441 | 1441 |
return err |
| 1442 | 1442 |
} |
| 1443 | 1443 |
|
| 1444 |
- if !term.IsTerminal(int(os.Stdin.Fd())) {
|
|
| 1444 |
+ if !term.IsTerminal(os.Stdin.Fd()) {
|
|
| 1445 | 1445 |
if err := <-sendStdin; err != nil {
|
| 1446 | 1446 |
return err |
| 1447 | 1447 |
} |
| ... | ... |
@@ -24,7 +24,7 @@ import ( |
| 24 | 24 |
type Container struct {
|
| 25 | 25 |
root string |
| 26 | 26 |
|
| 27 |
- Id string |
|
| 27 |
+ ID string |
|
| 28 | 28 |
|
| 29 | 29 |
Created time.Time |
| 30 | 30 |
|
| ... | ... |
@@ -168,8 +168,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet |
| 168 | 168 |
} |
| 169 | 169 |
|
| 170 | 170 |
type NetworkSettings struct {
|
| 171 |
- IpAddress string |
|
| 172 |
- IpPrefixLen int |
|
| 171 |
+ IPAddress string |
|
| 172 |
+ IPPrefixLen int |
|
| 173 | 173 |
Gateway string |
| 174 | 174 |
Bridge string |
| 175 | 175 |
PortMapping map[string]string |
| ... | ... |
@@ -410,7 +410,7 @@ func (container *Container) Start() error {
|
| 410 | 410 |
defer container.State.unlock() |
| 411 | 411 |
|
| 412 | 412 |
if container.State.Running {
|
| 413 |
- return fmt.Errorf("The container %s is already running.", container.Id)
|
|
| 413 |
+ return fmt.Errorf("The container %s is already running.", container.ID)
|
|
| 414 | 414 |
} |
| 415 | 415 |
if err := container.EnsureMounted(); err != nil {
|
| 416 | 416 |
return err |
| ... | ... |
@@ -432,24 +432,24 @@ func (container *Container) Start() error {
|
| 432 | 432 |
|
| 433 | 433 |
// Create the requested volumes volumes |
| 434 | 434 |
for volPath := range container.Config.Volumes {
|
| 435 |
- if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil {
|
|
| 435 |
+ c, err := container.runtime.volumes.Create(nil, container, "", "", nil) |
|
| 436 |
+ if err != nil {
|
|
| 436 | 437 |
return err |
| 437 |
- } else {
|
|
| 438 |
- if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
|
|
| 439 |
- return nil |
|
| 440 |
- } |
|
| 441 |
- container.Volumes[volPath] = c.Id |
|
| 442 | 438 |
} |
| 439 |
+ if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
|
|
| 440 |
+ return nil |
|
| 441 |
+ } |
|
| 442 |
+ container.Volumes[volPath] = c.ID |
|
| 443 | 443 |
} |
| 444 | 444 |
|
| 445 | 445 |
if container.Config.VolumesFrom != "" {
|
| 446 | 446 |
c := container.runtime.Get(container.Config.VolumesFrom) |
| 447 | 447 |
if c == nil {
|
| 448 |
- return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.Id)
|
|
| 448 |
+ return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
|
|
| 449 | 449 |
} |
| 450 | 450 |
for volPath, id := range c.Volumes {
|
| 451 | 451 |
if _, exists := container.Volumes[volPath]; exists {
|
| 452 |
- return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.Id)
|
|
| 452 |
+ return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.ID)
|
|
| 453 | 453 |
} |
| 454 | 454 |
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
|
| 455 | 455 |
return nil |
| ... | ... |
@@ -463,7 +463,7 @@ func (container *Container) Start() error {
|
| 463 | 463 |
} |
| 464 | 464 |
|
| 465 | 465 |
params := []string{
|
| 466 |
- "-n", container.Id, |
|
| 466 |
+ "-n", container.ID, |
|
| 467 | 467 |
"-f", container.lxcConfigPath(), |
| 468 | 468 |
"--", |
| 469 | 469 |
"/sbin/init", |
| ... | ... |
@@ -574,17 +574,17 @@ func (container *Container) allocateNetwork() error {
|
| 574 | 574 |
} |
| 575 | 575 |
container.NetworkSettings.PortMapping = make(map[string]string) |
| 576 | 576 |
for _, spec := range container.Config.PortSpecs {
|
| 577 |
- if nat, err := iface.AllocatePort(spec); err != nil {
|
|
| 577 |
+ nat, err := iface.AllocatePort(spec) |
|
| 578 |
+ if err != nil {
|
|
| 578 | 579 |
iface.Release() |
| 579 | 580 |
return err |
| 580 |
- } else {
|
|
| 581 |
- container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend) |
|
| 582 | 581 |
} |
| 582 |
+ container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend) |
|
| 583 | 583 |
} |
| 584 | 584 |
container.network = iface |
| 585 | 585 |
container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface |
| 586 |
- container.NetworkSettings.IpAddress = iface.IPNet.IP.String() |
|
| 587 |
- container.NetworkSettings.IpPrefixLen, _ = iface.IPNet.Mask.Size() |
|
| 586 |
+ container.NetworkSettings.IPAddress = iface.IPNet.IP.String() |
|
| 587 |
+ container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size() |
|
| 588 | 588 |
container.NetworkSettings.Gateway = iface.Gateway.String() |
| 589 | 589 |
return nil |
| 590 | 590 |
} |
| ... | ... |
@@ -598,16 +598,16 @@ func (container *Container) releaseNetwork() {
|
| 598 | 598 |
// FIXME: replace this with a control socket within docker-init |
| 599 | 599 |
func (container *Container) waitLxc() error {
|
| 600 | 600 |
for {
|
| 601 |
- if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
|
|
| 601 |
+ output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
|
|
| 602 |
+ if err != nil {
|
|
| 602 | 603 |
return err |
| 603 |
- } else {
|
|
| 604 |
- if !strings.Contains(string(output), "RUNNING") {
|
|
| 605 |
- return nil |
|
| 606 |
- } |
|
| 604 |
+ } |
|
| 605 |
+ if !strings.Contains(string(output), "RUNNING") {
|
|
| 606 |
+ return nil |
|
| 607 | 607 |
} |
| 608 | 608 |
time.Sleep(500 * time.Millisecond) |
| 609 | 609 |
} |
| 610 |
- return nil |
|
| 610 |
+ panic("Unreachable")
|
|
| 611 | 611 |
} |
| 612 | 612 |
|
| 613 | 613 |
func (container *Container) monitor() {
|
| ... | ... |
@@ -617,17 +617,17 @@ func (container *Container) monitor() {
|
| 617 | 617 |
// If the command does not exists, try to wait via lxc |
| 618 | 618 |
if container.cmd == nil {
|
| 619 | 619 |
if err := container.waitLxc(); err != nil {
|
| 620 |
- utils.Debugf("%s: Process: %s", container.Id, err)
|
|
| 620 |
+ utils.Debugf("%s: Process: %s", container.ID, err)
|
|
| 621 | 621 |
} |
| 622 | 622 |
} else {
|
| 623 | 623 |
if err := container.cmd.Wait(); err != nil {
|
| 624 | 624 |
// Discard the error as any signals or non 0 returns will generate an error |
| 625 |
- utils.Debugf("%s: Process: %s", container.Id, err)
|
|
| 625 |
+ utils.Debugf("%s: Process: %s", container.ID, err)
|
|
| 626 | 626 |
} |
| 627 | 627 |
} |
| 628 | 628 |
utils.Debugf("Process finished")
|
| 629 | 629 |
|
| 630 |
- var exitCode int = -1 |
|
| 630 |
+ exitCode := -1 |
|
| 631 | 631 |
if container.cmd != nil {
|
| 632 | 632 |
exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() |
| 633 | 633 |
} |
| ... | ... |
@@ -636,24 +636,24 @@ func (container *Container) monitor() {
|
| 636 | 636 |
container.releaseNetwork() |
| 637 | 637 |
if container.Config.OpenStdin {
|
| 638 | 638 |
if err := container.stdin.Close(); err != nil {
|
| 639 |
- utils.Debugf("%s: Error close stdin: %s", container.Id, err)
|
|
| 639 |
+ utils.Debugf("%s: Error close stdin: %s", container.ID, err)
|
|
| 640 | 640 |
} |
| 641 | 641 |
} |
| 642 | 642 |
if err := container.stdout.CloseWriters(); err != nil {
|
| 643 |
- utils.Debugf("%s: Error close stdout: %s", container.Id, err)
|
|
| 643 |
+ utils.Debugf("%s: Error close stdout: %s", container.ID, err)
|
|
| 644 | 644 |
} |
| 645 | 645 |
if err := container.stderr.CloseWriters(); err != nil {
|
| 646 |
- utils.Debugf("%s: Error close stderr: %s", container.Id, err)
|
|
| 646 |
+ utils.Debugf("%s: Error close stderr: %s", container.ID, err)
|
|
| 647 | 647 |
} |
| 648 | 648 |
|
| 649 | 649 |
if container.ptyMaster != nil {
|
| 650 | 650 |
if err := container.ptyMaster.Close(); err != nil {
|
| 651 |
- utils.Debugf("%s: Error closing Pty master: %s", container.Id, err)
|
|
| 651 |
+ utils.Debugf("%s: Error closing Pty master: %s", container.ID, err)
|
|
| 652 | 652 |
} |
| 653 | 653 |
} |
| 654 | 654 |
|
| 655 | 655 |
if err := container.Unmount(); err != nil {
|
| 656 |
- log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
|
|
| 656 |
+ log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
|
|
| 657 | 657 |
} |
| 658 | 658 |
|
| 659 | 659 |
// Re-create a brand new stdin pipe once the container exited |
| ... | ... |
@@ -674,7 +674,7 @@ func (container *Container) monitor() {
|
| 674 | 674 |
// This is because State.setStopped() has already been called, and has caused Wait() |
| 675 | 675 |
// to return. |
| 676 | 676 |
// FIXME: why are we serializing running state to disk in the first place? |
| 677 |
- //log.Printf("%s: Failed to dump configuration to the disk: %s", container.Id, err)
|
|
| 677 |
+ //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
|
|
| 678 | 678 |
} |
| 679 | 679 |
} |
| 680 | 680 |
|
| ... | ... |
@@ -684,17 +684,17 @@ func (container *Container) kill() error {
|
| 684 | 684 |
} |
| 685 | 685 |
|
| 686 | 686 |
// Sending SIGKILL to the process via lxc |
| 687 |
- output, err := exec.Command("lxc-kill", "-n", container.Id, "9").CombinedOutput()
|
|
| 687 |
+ output, err := exec.Command("lxc-kill", "-n", container.ID, "9").CombinedOutput()
|
|
| 688 | 688 |
if err != nil {
|
| 689 |
- log.Printf("error killing container %s (%s, %s)", container.Id, output, err)
|
|
| 689 |
+ log.Printf("error killing container %s (%s, %s)", container.ID, output, err)
|
|
| 690 | 690 |
} |
| 691 | 691 |
|
| 692 | 692 |
// 2. Wait for the process to die, in last resort, try to kill the process directly |
| 693 | 693 |
if err := container.WaitTimeout(10 * time.Second); err != nil {
|
| 694 | 694 |
if container.cmd == nil {
|
| 695 |
- return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.Id)
|
|
| 695 |
+ return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ID)
|
|
| 696 | 696 |
} |
| 697 |
- log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.Id)
|
|
| 697 |
+ log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.ID)
|
|
| 698 | 698 |
if err := container.cmd.Process.Kill(); err != nil {
|
| 699 | 699 |
return err |
| 700 | 700 |
} |
| ... | ... |
@@ -722,7 +722,7 @@ func (container *Container) Stop(seconds int) error {
|
| 722 | 722 |
} |
| 723 | 723 |
|
| 724 | 724 |
// 1. Send a SIGTERM |
| 725 |
- if output, err := exec.Command("lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
|
|
| 725 |
+ if output, err := exec.Command("lxc-kill", "-n", container.ID, "15").CombinedOutput(); err != nil {
|
|
| 726 | 726 |
log.Print(string(output)) |
| 727 | 727 |
log.Print("Failed to send SIGTERM to the process, force killing")
|
| 728 | 728 |
if err := container.kill(); err != nil {
|
| ... | ... |
@@ -732,7 +732,7 @@ func (container *Container) Stop(seconds int) error {
|
| 732 | 732 |
|
| 733 | 733 |
// 2. Wait for the process to exit on its own |
| 734 | 734 |
if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
|
| 735 |
- log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.Id, seconds)
|
|
| 735 |
+ log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
|
|
| 736 | 736 |
if err := container.kill(); err != nil {
|
| 737 | 737 |
return err |
| 738 | 738 |
} |
| ... | ... |
@@ -796,7 +796,8 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
|
| 796 | 796 |
case <-done: |
| 797 | 797 |
return nil |
| 798 | 798 |
} |
| 799 |
- panic("unreachable")
|
|
| 799 |
+ |
|
| 800 |
+ panic("Unreachable")
|
|
| 800 | 801 |
} |
| 801 | 802 |
|
| 802 | 803 |
func (container *Container) EnsureMounted() error {
|
| ... | ... |
@@ -839,16 +840,16 @@ func (container *Container) Unmount() error {
|
| 839 | 839 |
return Unmount(container.RootfsPath()) |
| 840 | 840 |
} |
| 841 | 841 |
|
| 842 |
-// ShortId returns a shorthand version of the container's id for convenience. |
|
| 842 |
+// ShortID returns a shorthand version of the container's id for convenience. |
|
| 843 | 843 |
// A collision with other container shorthands is very unlikely, but possible. |
| 844 | 844 |
// In case of a collision a lookup with Runtime.Get() will fail, and the caller |
| 845 | 845 |
// will need to use a langer prefix, or the full-length container Id. |
| 846 |
-func (container *Container) ShortId() string {
|
|
| 847 |
- return utils.TruncateId(container.Id) |
|
| 846 |
+func (container *Container) ShortID() string {
|
|
| 847 |
+ return utils.TruncateID(container.ID) |
|
| 848 | 848 |
} |
| 849 | 849 |
|
| 850 | 850 |
func (container *Container) logPath(name string) string {
|
| 851 |
- return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name))
|
|
| 851 |
+ return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
|
|
| 852 | 852 |
} |
| 853 | 853 |
|
| 854 | 854 |
func (container *Container) ReadLog(name string) (io.Reader, error) {
|
| ... | ... |
@@ -888,7 +889,7 @@ func (container *Container) rwPath() string {
|
| 888 | 888 |
return path.Join(container.root, "rw") |
| 889 | 889 |
} |
| 890 | 890 |
|
| 891 |
-func validateId(id string) error {
|
|
| 891 |
+func validateID(id string) error {
|
|
| 892 | 892 |
if id == "" {
|
| 893 | 893 |
return fmt.Errorf("Invalid empty id")
|
| 894 | 894 |
} |
| ... | ... |
@@ -14,7 +14,7 @@ import ( |
| 14 | 14 |
"time" |
| 15 | 15 |
) |
| 16 | 16 |
|
| 17 |
-func TestIdFormat(t *testing.T) {
|
|
| 17 |
+func TestIDFormat(t *testing.T) {
|
|
| 18 | 18 |
runtime, err := newTestRuntime() |
| 19 | 19 |
if err != nil {
|
| 20 | 20 |
t.Fatal(err) |
| ... | ... |
@@ -22,19 +22,19 @@ func TestIdFormat(t *testing.T) {
|
| 22 | 22 |
defer nuke(runtime) |
| 23 | 23 |
container1, err := NewBuilder(runtime).Create( |
| 24 | 24 |
&Config{
|
| 25 |
- Image: GetTestImage(runtime).Id, |
|
| 25 |
+ Image: GetTestImage(runtime).ID, |
|
| 26 | 26 |
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
|
| 27 | 27 |
}, |
| 28 | 28 |
) |
| 29 | 29 |
if err != nil {
|
| 30 | 30 |
t.Fatal(err) |
| 31 | 31 |
} |
| 32 |
- match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.Id))
|
|
| 32 |
+ match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.ID))
|
|
| 33 | 33 |
if err != nil {
|
| 34 | 34 |
t.Fatal(err) |
| 35 | 35 |
} |
| 36 | 36 |
if !match {
|
| 37 |
- t.Fatalf("Invalid container ID: %s", container1.Id)
|
|
| 37 |
+ t.Fatalf("Invalid container ID: %s", container1.ID)
|
|
| 38 | 38 |
} |
| 39 | 39 |
} |
| 40 | 40 |
|
| ... | ... |
@@ -46,7 +46,7 @@ func TestMultipleAttachRestart(t *testing.T) {
|
| 46 | 46 |
defer nuke(runtime) |
| 47 | 47 |
container, err := NewBuilder(runtime).Create( |
| 48 | 48 |
&Config{
|
| 49 |
- Image: GetTestImage(runtime).Id, |
|
| 49 |
+ Image: GetTestImage(runtime).ID, |
|
| 50 | 50 |
Cmd: []string{"/bin/sh", "-c",
|
| 51 | 51 |
"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"}, |
| 52 | 52 |
}, |
| ... | ... |
@@ -153,7 +153,7 @@ func TestDiff(t *testing.T) {
|
| 153 | 153 |
// Create a container and remove a file |
| 154 | 154 |
container1, err := builder.Create( |
| 155 | 155 |
&Config{
|
| 156 |
- Image: GetTestImage(runtime).Id, |
|
| 156 |
+ Image: GetTestImage(runtime).ID, |
|
| 157 | 157 |
Cmd: []string{"/bin/rm", "/etc/passwd"},
|
| 158 | 158 |
}, |
| 159 | 159 |
) |
| ... | ... |
@@ -194,7 +194,7 @@ func TestDiff(t *testing.T) {
|
| 194 | 194 |
// Create a new container from the commited image |
| 195 | 195 |
container2, err := builder.Create( |
| 196 | 196 |
&Config{
|
| 197 |
- Image: img.Id, |
|
| 197 |
+ Image: img.ID, |
|
| 198 | 198 |
Cmd: []string{"cat", "/etc/passwd"},
|
| 199 | 199 |
}, |
| 200 | 200 |
) |
| ... | ... |
@@ -217,6 +217,37 @@ func TestDiff(t *testing.T) {
|
| 217 | 217 |
t.Fatalf("/etc/passwd should not be present in the diff after commit.")
|
| 218 | 218 |
} |
| 219 | 219 |
} |
| 220 |
+ |
|
| 221 |
+ // Create a new containere |
|
| 222 |
+ container3, err := builder.Create( |
|
| 223 |
+ &Config{
|
|
| 224 |
+ Image: GetTestImage(runtime).ID, |
|
| 225 |
+ Cmd: []string{"rm", "/bin/httpd"},
|
|
| 226 |
+ }, |
|
| 227 |
+ ) |
|
| 228 |
+ if err != nil {
|
|
| 229 |
+ t.Fatal(err) |
|
| 230 |
+ } |
|
| 231 |
+ defer runtime.Destroy(container3) |
|
| 232 |
+ |
|
| 233 |
+ if err := container3.Run(); err != nil {
|
|
| 234 |
+ t.Fatal(err) |
|
| 235 |
+ } |
|
| 236 |
+ |
|
| 237 |
+ // Check the changelog |
|
| 238 |
+ c, err = container3.Changes() |
|
| 239 |
+ if err != nil {
|
|
| 240 |
+ t.Fatal(err) |
|
| 241 |
+ } |
|
| 242 |
+ success = false |
|
| 243 |
+ for _, elem := range c {
|
|
| 244 |
+ if elem.Path == "/bin/httpd" && elem.Kind == 2 {
|
|
| 245 |
+ success = true |
|
| 246 |
+ } |
|
| 247 |
+ } |
|
| 248 |
+ if !success {
|
|
| 249 |
+ t.Fatalf("/bin/httpd should be present in the diff after commit.")
|
|
| 250 |
+ } |
|
| 220 | 251 |
} |
| 221 | 252 |
|
| 222 | 253 |
func TestCommitAutoRun(t *testing.T) {
|
| ... | ... |
@@ -229,7 +260,7 @@ func TestCommitAutoRun(t *testing.T) {
|
| 229 | 229 |
builder := NewBuilder(runtime) |
| 230 | 230 |
container1, err := builder.Create( |
| 231 | 231 |
&Config{
|
| 232 |
- Image: GetTestImage(runtime).Id, |
|
| 232 |
+ Image: GetTestImage(runtime).ID, |
|
| 233 | 233 |
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
|
| 234 | 234 |
}, |
| 235 | 235 |
) |
| ... | ... |
@@ -260,7 +291,7 @@ func TestCommitAutoRun(t *testing.T) {
|
| 260 | 260 |
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world |
| 261 | 261 |
container2, err := builder.Create( |
| 262 | 262 |
&Config{
|
| 263 |
- Image: img.Id, |
|
| 263 |
+ Image: img.ID, |
|
| 264 | 264 |
}, |
| 265 | 265 |
) |
| 266 | 266 |
if err != nil {
|
| ... | ... |
@@ -309,7 +340,7 @@ func TestCommitRun(t *testing.T) {
|
| 309 | 309 |
|
| 310 | 310 |
container1, err := builder.Create( |
| 311 | 311 |
&Config{
|
| 312 |
- Image: GetTestImage(runtime).Id, |
|
| 312 |
+ Image: GetTestImage(runtime).ID, |
|
| 313 | 313 |
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
|
| 314 | 314 |
}, |
| 315 | 315 |
) |
| ... | ... |
@@ -341,7 +372,7 @@ func TestCommitRun(t *testing.T) {
|
| 341 | 341 |
|
| 342 | 342 |
container2, err := builder.Create( |
| 343 | 343 |
&Config{
|
| 344 |
- Image: img.Id, |
|
| 344 |
+ Image: img.ID, |
|
| 345 | 345 |
Cmd: []string{"cat", "/world"},
|
| 346 | 346 |
}, |
| 347 | 347 |
) |
| ... | ... |
@@ -388,7 +419,7 @@ func TestStart(t *testing.T) {
|
| 388 | 388 |
defer nuke(runtime) |
| 389 | 389 |
container, err := NewBuilder(runtime).Create( |
| 390 | 390 |
&Config{
|
| 391 |
- Image: GetTestImage(runtime).Id, |
|
| 391 |
+ Image: GetTestImage(runtime).ID, |
|
| 392 | 392 |
Memory: 33554432, |
| 393 | 393 |
CpuShares: 1000, |
| 394 | 394 |
Cmd: []string{"/bin/cat"},
|
| ... | ... |
@@ -432,7 +463,7 @@ func TestRun(t *testing.T) {
|
| 432 | 432 |
defer nuke(runtime) |
| 433 | 433 |
container, err := NewBuilder(runtime).Create( |
| 434 | 434 |
&Config{
|
| 435 |
- Image: GetTestImage(runtime).Id, |
|
| 435 |
+ Image: GetTestImage(runtime).ID, |
|
| 436 | 436 |
Cmd: []string{"ls", "-al"},
|
| 437 | 437 |
}, |
| 438 | 438 |
) |
| ... | ... |
@@ -460,7 +491,7 @@ func TestOutput(t *testing.T) {
|
| 460 | 460 |
defer nuke(runtime) |
| 461 | 461 |
container, err := NewBuilder(runtime).Create( |
| 462 | 462 |
&Config{
|
| 463 |
- Image: GetTestImage(runtime).Id, |
|
| 463 |
+ Image: GetTestImage(runtime).ID, |
|
| 464 | 464 |
Cmd: []string{"echo", "-n", "foobar"},
|
| 465 | 465 |
}, |
| 466 | 466 |
) |
| ... | ... |
@@ -484,7 +515,7 @@ func TestKillDifferentUser(t *testing.T) {
|
| 484 | 484 |
} |
| 485 | 485 |
defer nuke(runtime) |
| 486 | 486 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 487 |
- Image: GetTestImage(runtime).Id, |
|
| 487 |
+ Image: GetTestImage(runtime).ID, |
|
| 488 | 488 |
Cmd: []string{"tail", "-f", "/etc/resolv.conf"},
|
| 489 | 489 |
User: "daemon", |
| 490 | 490 |
}, |
| ... | ... |
@@ -532,7 +563,7 @@ func TestKill(t *testing.T) {
|
| 532 | 532 |
} |
| 533 | 533 |
defer nuke(runtime) |
| 534 | 534 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 535 |
- Image: GetTestImage(runtime).Id, |
|
| 535 |
+ Image: GetTestImage(runtime).ID, |
|
| 536 | 536 |
Cmd: []string{"cat", "/dev/zero"},
|
| 537 | 537 |
}, |
| 538 | 538 |
) |
| ... | ... |
@@ -580,7 +611,7 @@ func TestExitCode(t *testing.T) {
|
| 580 | 580 |
builder := NewBuilder(runtime) |
| 581 | 581 |
|
| 582 | 582 |
trueContainer, err := builder.Create(&Config{
|
| 583 |
- Image: GetTestImage(runtime).Id, |
|
| 583 |
+ Image: GetTestImage(runtime).ID, |
|
| 584 | 584 |
Cmd: []string{"/bin/true", ""},
|
| 585 | 585 |
}) |
| 586 | 586 |
if err != nil {
|
| ... | ... |
@@ -595,7 +626,7 @@ func TestExitCode(t *testing.T) {
|
| 595 | 595 |
} |
| 596 | 596 |
|
| 597 | 597 |
falseContainer, err := builder.Create(&Config{
|
| 598 |
- Image: GetTestImage(runtime).Id, |
|
| 598 |
+ Image: GetTestImage(runtime).ID, |
|
| 599 | 599 |
Cmd: []string{"/bin/false", ""},
|
| 600 | 600 |
}) |
| 601 | 601 |
if err != nil {
|
| ... | ... |
@@ -617,7 +648,7 @@ func TestRestart(t *testing.T) {
|
| 617 | 617 |
} |
| 618 | 618 |
defer nuke(runtime) |
| 619 | 619 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 620 |
- Image: GetTestImage(runtime).Id, |
|
| 620 |
+ Image: GetTestImage(runtime).ID, |
|
| 621 | 621 |
Cmd: []string{"echo", "-n", "foobar"},
|
| 622 | 622 |
}, |
| 623 | 623 |
) |
| ... | ... |
@@ -650,7 +681,7 @@ func TestRestartStdin(t *testing.T) {
|
| 650 | 650 |
} |
| 651 | 651 |
defer nuke(runtime) |
| 652 | 652 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 653 |
- Image: GetTestImage(runtime).Id, |
|
| 653 |
+ Image: GetTestImage(runtime).ID, |
|
| 654 | 654 |
Cmd: []string{"cat"},
|
| 655 | 655 |
|
| 656 | 656 |
OpenStdin: true, |
| ... | ... |
@@ -732,7 +763,7 @@ func TestUser(t *testing.T) {
|
| 732 | 732 |
|
| 733 | 733 |
// Default user must be root |
| 734 | 734 |
container, err := builder.Create(&Config{
|
| 735 |
- Image: GetTestImage(runtime).Id, |
|
| 735 |
+ Image: GetTestImage(runtime).ID, |
|
| 736 | 736 |
Cmd: []string{"id"},
|
| 737 | 737 |
}, |
| 738 | 738 |
) |
| ... | ... |
@@ -750,7 +781,7 @@ func TestUser(t *testing.T) {
|
| 750 | 750 |
|
| 751 | 751 |
// Set a username |
| 752 | 752 |
container, err = builder.Create(&Config{
|
| 753 |
- Image: GetTestImage(runtime).Id, |
|
| 753 |
+ Image: GetTestImage(runtime).ID, |
|
| 754 | 754 |
Cmd: []string{"id"},
|
| 755 | 755 |
|
| 756 | 756 |
User: "root", |
| ... | ... |
@@ -770,7 +801,7 @@ func TestUser(t *testing.T) {
|
| 770 | 770 |
|
| 771 | 771 |
// Set a UID |
| 772 | 772 |
container, err = builder.Create(&Config{
|
| 773 |
- Image: GetTestImage(runtime).Id, |
|
| 773 |
+ Image: GetTestImage(runtime).ID, |
|
| 774 | 774 |
Cmd: []string{"id"},
|
| 775 | 775 |
|
| 776 | 776 |
User: "0", |
| ... | ... |
@@ -790,7 +821,7 @@ func TestUser(t *testing.T) {
|
| 790 | 790 |
|
| 791 | 791 |
// Set a different user by uid |
| 792 | 792 |
container, err = builder.Create(&Config{
|
| 793 |
- Image: GetTestImage(runtime).Id, |
|
| 793 |
+ Image: GetTestImage(runtime).ID, |
|
| 794 | 794 |
Cmd: []string{"id"},
|
| 795 | 795 |
|
| 796 | 796 |
User: "1", |
| ... | ... |
@@ -812,7 +843,7 @@ func TestUser(t *testing.T) {
|
| 812 | 812 |
|
| 813 | 813 |
// Set a different user by username |
| 814 | 814 |
container, err = builder.Create(&Config{
|
| 815 |
- Image: GetTestImage(runtime).Id, |
|
| 815 |
+ Image: GetTestImage(runtime).ID, |
|
| 816 | 816 |
Cmd: []string{"id"},
|
| 817 | 817 |
|
| 818 | 818 |
User: "daemon", |
| ... | ... |
@@ -841,7 +872,7 @@ func TestMultipleContainers(t *testing.T) {
|
| 841 | 841 |
builder := NewBuilder(runtime) |
| 842 | 842 |
|
| 843 | 843 |
container1, err := builder.Create(&Config{
|
| 844 |
- Image: GetTestImage(runtime).Id, |
|
| 844 |
+ Image: GetTestImage(runtime).ID, |
|
| 845 | 845 |
Cmd: []string{"cat", "/dev/zero"},
|
| 846 | 846 |
}, |
| 847 | 847 |
) |
| ... | ... |
@@ -851,7 +882,7 @@ func TestMultipleContainers(t *testing.T) {
|
| 851 | 851 |
defer runtime.Destroy(container1) |
| 852 | 852 |
|
| 853 | 853 |
container2, err := builder.Create(&Config{
|
| 854 |
- Image: GetTestImage(runtime).Id, |
|
| 854 |
+ Image: GetTestImage(runtime).ID, |
|
| 855 | 855 |
Cmd: []string{"cat", "/dev/zero"},
|
| 856 | 856 |
}, |
| 857 | 857 |
) |
| ... | ... |
@@ -897,7 +928,7 @@ func TestStdin(t *testing.T) {
|
| 897 | 897 |
} |
| 898 | 898 |
defer nuke(runtime) |
| 899 | 899 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 900 |
- Image: GetTestImage(runtime).Id, |
|
| 900 |
+ Image: GetTestImage(runtime).ID, |
|
| 901 | 901 |
Cmd: []string{"cat"},
|
| 902 | 902 |
|
| 903 | 903 |
OpenStdin: true, |
| ... | ... |
@@ -944,7 +975,7 @@ func TestTty(t *testing.T) {
|
| 944 | 944 |
} |
| 945 | 945 |
defer nuke(runtime) |
| 946 | 946 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 947 |
- Image: GetTestImage(runtime).Id, |
|
| 947 |
+ Image: GetTestImage(runtime).ID, |
|
| 948 | 948 |
Cmd: []string{"cat"},
|
| 949 | 949 |
|
| 950 | 950 |
OpenStdin: true, |
| ... | ... |
@@ -991,7 +1022,7 @@ func TestEnv(t *testing.T) {
|
| 991 | 991 |
} |
| 992 | 992 |
defer nuke(runtime) |
| 993 | 993 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 994 |
- Image: GetTestImage(runtime).Id, |
|
| 994 |
+ Image: GetTestImage(runtime).ID, |
|
| 995 | 995 |
Cmd: []string{"/usr/bin/env"},
|
| 996 | 996 |
}, |
| 997 | 997 |
) |
| ... | ... |
@@ -1069,7 +1100,7 @@ func TestLXCConfig(t *testing.T) {
|
| 1069 | 1069 |
cpuMax := 10000 |
| 1070 | 1070 |
cpu := cpuMin + rand.Intn(cpuMax-cpuMin) |
| 1071 | 1071 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 1072 |
- Image: GetTestImage(runtime).Id, |
|
| 1072 |
+ Image: GetTestImage(runtime).ID, |
|
| 1073 | 1073 |
Cmd: []string{"/bin/true"},
|
| 1074 | 1074 |
|
| 1075 | 1075 |
Hostname: "foobar", |
| ... | ... |
@@ -1097,7 +1128,7 @@ func BenchmarkRunSequencial(b *testing.B) {
|
| 1097 | 1097 |
defer nuke(runtime) |
| 1098 | 1098 |
for i := 0; i < b.N; i++ {
|
| 1099 | 1099 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 1100 |
- Image: GetTestImage(runtime).Id, |
|
| 1100 |
+ Image: GetTestImage(runtime).ID, |
|
| 1101 | 1101 |
Cmd: []string{"echo", "-n", "foo"},
|
| 1102 | 1102 |
}, |
| 1103 | 1103 |
) |
| ... | ... |
@@ -1132,7 +1163,7 @@ func BenchmarkRunParallel(b *testing.B) {
|
| 1132 | 1132 |
tasks = append(tasks, complete) |
| 1133 | 1133 |
go func(i int, complete chan error) {
|
| 1134 | 1134 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 1135 |
- Image: GetTestImage(runtime).Id, |
|
| 1135 |
+ Image: GetTestImage(runtime).ID, |
|
| 1136 | 1136 |
Cmd: []string{"echo", "-n", "foo"},
|
| 1137 | 1137 |
}, |
| 1138 | 1138 |
) |
| ... | ... |
@@ -11,13 +11,13 @@ import ( |
| 11 | 11 |
"time" |
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 |
-var DOCKER_PATH string = path.Join(os.Getenv("DOCKERPATH"), "docker")
|
|
| 14 |
+var DOCKERPATH = path.Join(os.Getenv("DOCKERPATH"), "docker")
|
|
| 15 | 15 |
|
| 16 | 16 |
// WARNING: this crashTest will 1) crash your host, 2) remove all containers |
| 17 | 17 |
func runDaemon() (*exec.Cmd, error) {
|
| 18 | 18 |
os.Remove("/var/run/docker.pid")
|
| 19 | 19 |
exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
|
| 20 |
- cmd := exec.Command(DOCKER_PATH, "-d") |
|
| 20 |
+ cmd := exec.Command(DOCKERPATH, "-d") |
|
| 21 | 21 |
outPipe, err := cmd.StdoutPipe() |
| 22 | 22 |
if err != nil {
|
| 23 | 23 |
return nil, err |
| ... | ... |
@@ -77,7 +77,7 @@ func crashTest() error {
|
| 77 | 77 |
stop = false |
| 78 | 78 |
for i := 0; i < 100 && !stop; {
|
| 79 | 79 |
func() error {
|
| 80 |
- cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
|
|
| 80 |
+ cmd := exec.Command(DOCKERPATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
|
|
| 81 | 81 |
i++ |
| 82 | 82 |
totalTestCount++ |
| 83 | 83 |
outPipe, err := cmd.StdoutPipe() |
| ... | ... |
@@ -2,18 +2,15 @@ |
| 2 | 2 |
set -e |
| 3 | 3 |
|
| 4 | 4 |
# these should match the names found at http://www.debian.org/releases/ |
| 5 |
-stableSuite='squeeze' |
|
| 6 |
-testingSuite='wheezy' |
|
| 5 |
+stableSuite='wheezy' |
|
| 6 |
+testingSuite='jessie' |
|
| 7 | 7 |
unstableSuite='sid' |
| 8 | 8 |
|
| 9 |
-# if suite is equal to this, it gets the "latest" tag |
|
| 10 |
-latestSuite="$testingSuite" |
|
| 11 |
- |
|
| 12 | 9 |
variant='minbase' |
| 13 | 10 |
include='iproute,iputils-ping' |
| 14 | 11 |
|
| 15 | 12 |
repo="$1" |
| 16 |
-suite="${2:-$latestSuite}"
|
|
| 13 |
+suite="${2:-$stableSuite}"
|
|
| 17 | 14 |
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
|
| 18 | 15 |
|
| 19 | 16 |
if [ ! "$repo" ]; then |
| ... | ... |
@@ -41,17 +38,14 @@ img=$(sudo tar -c . | docker import -) |
| 41 | 41 |
# tag suite |
| 42 | 42 |
docker tag $img $repo $suite |
| 43 | 43 |
|
| 44 |
-if [ "$suite" = "$latestSuite" ]; then |
|
| 45 |
- # tag latest |
|
| 46 |
- docker tag $img $repo latest |
|
| 47 |
-fi |
|
| 48 |
- |
|
| 49 | 44 |
# test the image |
| 50 | 45 |
docker run -i -t $repo:$suite echo success |
| 51 | 46 |
|
| 52 |
-# unstable's version numbers match testing (since it's mostly just a sandbox for testing), so it doesn't get a version number tag |
|
| 53 |
-if [ "$suite" != "$unstableSuite" -a "$suite" != 'unstable' ]; then |
|
| 54 |
- # tag the specific version |
|
| 47 |
+if [ "$suite" = "$stableSuite" -o "$suite" = 'stable' ]; then |
|
| 48 |
+ # tag latest |
|
| 49 |
+ docker tag $img $repo latest |
|
| 50 |
+ |
|
| 51 |
+ # tag the specific debian release version |
|
| 55 | 52 |
ver=$(docker run $repo:$suite cat /etc/debian_version) |
| 56 | 53 |
docker tag $img $repo $ver |
| 57 | 54 |
fi |
| ... | ... |
@@ -15,7 +15,7 @@ import ( |
| 15 | 15 |
) |
| 16 | 16 |
|
| 17 | 17 |
var ( |
| 18 |
- GIT_COMMIT string |
|
| 18 |
+ GITCOMMIT string |
|
| 19 | 19 |
) |
| 20 | 20 |
|
| 21 | 21 |
func main() {
|
| ... | ... |
@@ -59,7 +59,7 @@ func main() {
|
| 59 | 59 |
if *flDebug {
|
| 60 | 60 |
os.Setenv("DEBUG", "1")
|
| 61 | 61 |
} |
| 62 |
- docker.GIT_COMMIT = GIT_COMMIT |
|
| 62 |
+ docker.GITCOMMIT = GITCOMMIT |
|
| 63 | 63 |
if *flDaemon {
|
| 64 | 64 |
if flag.NArg() != 0 {
|
| 65 | 65 |
flag.Usage() |
| ... | ... |
@@ -6,6 +6,7 @@ SPHINXOPTS = |
| 6 | 6 |
SPHINXBUILD = sphinx-build |
| 7 | 7 |
PAPER = |
| 8 | 8 |
BUILDDIR = _build |
| 9 |
+PYTHON = python |
|
| 9 | 10 |
|
| 10 | 11 |
# Internal variables. |
| 11 | 12 |
PAPEROPT_a4 = -D latex_paper_size=a4 |
| ... | ... |
@@ -38,6 +39,7 @@ help: |
| 38 | 38 |
# @echo " linkcheck to check all external links for integrity" |
| 39 | 39 |
# @echo " doctest to run all doctests embedded in the documentation (if enabled)" |
| 40 | 40 |
@echo " docs to build the docs and copy the static files to the outputdir" |
| 41 |
+ @echo " server to serve the docs in your browser under \`http://localhost:8000\`" |
|
| 41 | 42 |
@echo " publish to publish the app to dotcloud" |
| 42 | 43 |
|
| 43 | 44 |
clean: |
| ... | ... |
@@ -49,6 +51,8 @@ docs: |
| 49 | 49 |
@echo |
| 50 | 50 |
@echo "Build finished. The documentation pages are now in $(BUILDDIR)/html." |
| 51 | 51 |
|
| 52 |
+server: |
|
| 53 |
+ @cd $(BUILDDIR)/html; $(PYTHON) -m SimpleHTTPServer 8000 |
|
| 52 | 54 |
|
| 53 | 55 |
site: |
| 54 | 56 |
cp -r website $(BUILDDIR)/ |
| ... | ... |
@@ -59,7 +63,7 @@ site: |
| 59 | 59 |
connect: |
| 60 | 60 |
@echo connecting dotcloud to www.docker.io website, make sure to use user 1 |
| 61 | 61 |
@cd _build/website/ ; \ |
| 62 |
- dotcloud connect dockerwebsite ; |
|
| 62 |
+ dotcloud connect dockerwebsite ; \ |
|
| 63 | 63 |
dotcloud list |
| 64 | 64 |
|
| 65 | 65 |
push: |
| ... | ... |
@@ -14,20 +14,22 @@ Installation |
| 14 | 14 |
------------ |
| 15 | 15 |
|
| 16 | 16 |
* Work in your own fork of the code, we accept pull requests. |
| 17 |
-* Install sphinx: ``pip install sphinx`` |
|
| 18 |
-* Install sphinx httpdomain contrib package ``sphinxcontrib-httpdomain`` |
|
| 17 |
+* Install sphinx: `pip install sphinx` |
|
| 18 |
+ * Mac OS X: `[sudo] pip-2.7 install sphinx`) |
|
| 19 |
+* Install sphinx httpdomain contrib package: `pip install sphinxcontrib-httpdomain` |
|
| 20 |
+ * Mac OS X: `[sudo] pip-2.7 install sphinxcontrib-httpdomain` |
|
| 19 | 21 |
* If pip is not available you can probably install it using your favorite package manager as **python-pip** |
| 20 | 22 |
|
| 21 | 23 |
Usage |
| 22 | 24 |
----- |
| 23 |
-* change the .rst files with your favorite editor to your liking |
|
| 24 |
-* run *make docs* to clean up old files and generate new ones |
|
| 25 |
-* your static website can now be found in the _build dir |
|
| 26 |
-* to preview what you have generated, cd into _build/html and then run 'python -m SimpleHTTPServer 8000' |
|
| 25 |
+* Change the `.rst` files with your favorite editor to your liking. |
|
| 26 |
+* Run `make docs` to clean up old files and generate new ones. |
|
| 27 |
+* Your static website can now be found in the `_build` directory. |
|
| 28 |
+* To preview what you have generated run `make server` and open <http://localhost:8000/> in your favorite browser. |
|
| 27 | 29 |
|
| 28 |
-Working using github's file editor |
|
| 30 |
+Working using GitHub's file editor |
|
| 29 | 31 |
---------------------------------- |
| 30 |
-Alternatively, for small changes and typo's you might want to use github's built in file editor. It allows |
|
| 32 |
+Alternatively, for small changes and typo's you might want to use GitHub's built in file editor. It allows |
|
| 31 | 33 |
you to preview your changes right online. Just be carefull not to create many commits. |
| 32 | 34 |
|
| 33 | 35 |
Images |
| ... | ... |
@@ -72,4 +74,4 @@ Guides on using sphinx |
| 72 | 72 |
|
| 73 | 73 |
* Code examples |
| 74 | 74 |
|
| 75 |
- Start without $, so it's easy to copy and paste. |
|
| 76 | 75 |
\ No newline at end of file |
| 76 |
+ Start without $, so it's easy to copy and paste. |
| ... | ... |
@@ -15,10 +15,17 @@ Docker Remote API |
| 15 | 15 |
- Default port in the docker deamon is 4243 |
| 16 | 16 |
- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr |
| 17 | 17 |
|
| 18 |
-2. Endpoints |
|
| 18 |
+2. Version |
|
| 19 |
+========== |
|
| 20 |
+ |
|
| 21 |
+The current verson of the API is 1.1 |
|
| 22 |
+Calling /images/<name>/insert is the same as calling /v1.1/images/<name>/insert |
|
| 23 |
+You can still call an old version of the api using /v1.0/images/<name>/insert |
|
| 24 |
+ |
|
| 25 |
+3. Endpoints |
|
| 19 | 26 |
============ |
| 20 | 27 |
|
| 21 |
-2.1 Containers |
|
| 28 |
+3.1 Containers |
|
| 22 | 29 |
-------------- |
| 23 | 30 |
|
| 24 | 31 |
List containers |
| ... | ... |
@@ -132,6 +139,7 @@ Create a container |
| 132 | 132 |
:jsonparam config: the container's configuration |
| 133 | 133 |
:statuscode 201: no error |
| 134 | 134 |
:statuscode 404: no such container |
| 135 |
+ :statuscode 406: impossible to attach (container not running) |
|
| 135 | 136 |
:statuscode 500: server error |
| 136 | 137 |
|
| 137 | 138 |
|
| ... | ... |
@@ -459,7 +467,7 @@ Remove a container |
| 459 | 459 |
:statuscode 500: server error |
| 460 | 460 |
|
| 461 | 461 |
|
| 462 |
-2.2 Images |
|
| 462 |
+3.2 Images |
|
| 463 | 463 |
---------- |
| 464 | 464 |
|
| 465 | 465 |
List Images |
| ... | ... |
@@ -548,7 +556,19 @@ Create an image |
| 548 | 548 |
|
| 549 | 549 |
POST /images/create?fromImage=base HTTP/1.1 |
| 550 | 550 |
|
| 551 |
- **Example response**: |
|
| 551 |
+ **Example response v1.1**: |
|
| 552 |
+ |
|
| 553 |
+ .. sourcecode:: http |
|
| 554 |
+ |
|
| 555 |
+ HTTP/1.1 200 OK |
|
| 556 |
+ Content-Type: application/json |
|
| 557 |
+ |
|
| 558 |
+ {"status":"Pulling..."}
|
|
| 559 |
+ {"status":"Pulling", "progress":"1/? (n/a)"}
|
|
| 560 |
+ {"error":"Invalid..."}
|
|
| 561 |
+ ... |
|
| 562 |
+ |
|
| 563 |
+ **Example response v1.0**: |
|
| 552 | 564 |
|
| 553 | 565 |
.. sourcecode:: http |
| 554 | 566 |
|
| ... | ... |
@@ -579,7 +599,19 @@ Insert a file in a image |
| 579 | 579 |
|
| 580 | 580 |
POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 |
| 581 | 581 |
|
| 582 |
- **Example response**: |
|
| 582 |
+ **Example response v1.1**: |
|
| 583 |
+ |
|
| 584 |
+ .. sourcecode:: http |
|
| 585 |
+ |
|
| 586 |
+ HTTP/1.1 200 OK |
|
| 587 |
+ Content-Type: application/json |
|
| 588 |
+ |
|
| 589 |
+ {"status":"Inserting..."}
|
|
| 590 |
+ {"status":"Inserting", "progress":"1/? (n/a)"}
|
|
| 591 |
+ {"error":"Invalid..."}
|
|
| 592 |
+ ... |
|
| 593 |
+ |
|
| 594 |
+ **Example response v1.0**: |
|
| 583 | 595 |
|
| 584 | 596 |
.. sourcecode:: http |
| 585 | 597 |
|
| ... | ... |
@@ -694,7 +726,19 @@ Push an image on the registry |
| 694 | 694 |
|
| 695 | 695 |
POST /images/test/push HTTP/1.1 |
| 696 | 696 |
|
| 697 |
- **Example response**: |
|
| 697 |
+ **Example response v1.1**: |
|
| 698 |
+ |
|
| 699 |
+ .. sourcecode:: http |
|
| 700 |
+ |
|
| 701 |
+ HTTP/1.1 200 OK |
|
| 702 |
+ Content-Type: application/json |
|
| 703 |
+ |
|
| 704 |
+ {"status":"Pushing..."}
|
|
| 705 |
+ {"status":"Pushing", "progress":"1/? (n/a)"}
|
|
| 706 |
+ {"error":"Invalid..."}
|
|
| 707 |
+ ... |
|
| 708 |
+ |
|
| 709 |
+ **Example response v1.0**: |
|
| 698 | 710 |
|
| 699 | 711 |
.. sourcecode:: http |
| 700 | 712 |
|
| ... | ... |
@@ -800,7 +844,7 @@ Search images |
| 800 | 800 |
:statuscode 500: server error |
| 801 | 801 |
|
| 802 | 802 |
|
| 803 |
-2.3 Misc |
|
| 803 |
+3.3 Misc |
|
| 804 | 804 |
-------- |
| 805 | 805 |
|
| 806 | 806 |
Build an image from Dockerfile via stdin |
| ... | ... |
@@ -826,6 +870,7 @@ Build an image from Dockerfile via stdin |
| 826 | 826 |
|
| 827 | 827 |
{{ STREAM }}
|
| 828 | 828 |
|
| 829 |
+ :query t: tag to be applied to the resulting image in case of success |
|
| 829 | 830 |
:statuscode 200: no error |
| 830 | 831 |
:statuscode 500: server error |
| 831 | 832 |
|
| ... | ... |
@@ -912,10 +957,12 @@ Display system-wide information |
| 912 | 912 |
|
| 913 | 913 |
{
|
| 914 | 914 |
"Containers":11, |
| 915 |
- "Version":"0.2.2", |
|
| 916 | 915 |
"Images":16, |
| 917 |
- "GoVersion":"go1.0.3", |
|
| 918 |
- "Debug":false |
|
| 916 |
+ "Debug":false, |
|
| 917 |
+ "NFd": 11, |
|
| 918 |
+ "NGoroutines":21, |
|
| 919 |
+ "MemoryLimit":true, |
|
| 920 |
+ "SwapLimit":false |
|
| 919 | 921 |
} |
| 920 | 922 |
|
| 921 | 923 |
:statuscode 200: no error |
| ... | ... |
@@ -941,12 +988,11 @@ Show the docker version information |
| 941 | 941 |
|
| 942 | 942 |
HTTP/1.1 200 OK |
| 943 | 943 |
Content-Type: application/json |
| 944 |
- |
|
| 944 |
+ |
|
| 945 | 945 |
{
|
| 946 | 946 |
"Version":"0.2.2", |
| 947 | 947 |
"GitCommit":"5a2a5cc+CHANGES", |
| 948 |
- "MemoryLimit":true, |
|
| 949 |
- "SwapLimit":false |
|
| 948 |
+ "GoVersion":"go1.0.3" |
|
| 950 | 949 |
} |
| 951 | 950 |
|
| 952 | 951 |
:statuscode 200: no error |
| ... | ... |
@@ -246,7 +246,6 @@ The Index has two main purposes (along with its fancy social features): |
| 246 | 246 |
|
| 247 | 247 |
- Resolve short names (to avoid passing absolute URLs all the time) |
| 248 | 248 |
- username/projectname -> \https://registry.docker.io/users/<username>/repositories/<projectname>/ |
| 249 |
- - team/projectname -> \https://registry.docker.io/team/<team>/repositories/<projectname>/ |
|
| 250 | 249 |
- Authenticate a user as a repos owner (for a central referenced repository) |
| 251 | 250 |
|
| 252 | 251 |
3.1 Without an Index |
| ... | ... |
@@ -2,12 +2,27 @@ |
| 2 | 2 |
:description: Build a new image from the Dockerfile passed via stdin |
| 3 | 3 |
:keywords: build, docker, container, documentation |
| 4 | 4 |
|
| 5 |
-======================================================== |
|
| 6 |
-``build`` -- Build a container from Dockerfile via stdin |
|
| 7 |
-======================================================== |
|
| 5 |
+================================================ |
|
| 6 |
+``build`` -- Build a container from a Dockerfile |
|
| 7 |
+================================================ |
|
| 8 | 8 |
|
| 9 | 9 |
:: |
| 10 | 10 |
|
| 11 |
- Usage: docker build - |
|
| 12 |
- Example: cat Dockerfile | docker build - |
|
| 13 |
- Build a new image from the Dockerfile passed via stdin |
|
| 11 |
+ Usage: docker build [OPTIONS] PATH | - |
|
| 12 |
+ Build a new container image from the source code at PATH |
|
| 13 |
+ -t="": Tag to be applied to the resulting image in case of success. |
|
| 14 |
+ |
|
| 15 |
+Examples |
|
| 16 |
+-------- |
|
| 17 |
+ |
|
| 18 |
+.. code-block:: bash |
|
| 19 |
+ |
|
| 20 |
+ docker build . |
|
| 21 |
+ |
|
| 22 |
+This will take the local Dockerfile |
|
| 23 |
+ |
|
| 24 |
+.. code-block:: bash |
|
| 25 |
+ |
|
| 26 |
+ docker build - |
|
| 27 |
+ |
|
| 28 |
+This will read a Dockerfile form Stdin without context |
| ... | ... |
@@ -5,8 +5,8 @@ |
| 5 | 5 |
Introduction |
| 6 | 6 |
============ |
| 7 | 7 |
|
| 8 |
-Docker - The Linux container runtime |
|
| 8 |
+Docker -- The Linux container runtime |
|
| 9 |
+------------------------------------- |
|
| 9 | 10 |
|
| 10 | 11 |
Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers. |
| 11 | 12 |
|
| ... | ... |
@@ -1,8 +1,8 @@ |
| 1 |
-:title: Setting up a dev environment |
|
| 1 |
+:title: Setting Up a Dev Environment |
|
| 2 | 2 |
:description: Guides on how to contribute to docker |
| 3 | 3 |
:keywords: Docker, documentation, developers, contributing, dev environment |
| 4 | 4 |
|
| 5 |
-Setting up a dev environment |
|
| 5 |
+Setting Up a Dev Environment |
|
| 6 | 6 |
============================ |
| 7 | 7 |
|
| 8 | 8 |
Instructions that have been verified to work on Ubuntu 12.10, |
| ... | ... |
@@ -1,6 +1,6 @@ |
| 1 | 1 |
:title: Docker Examples |
| 2 | 2 |
:description: Examples on how to use Docker |
| 3 |
-:keywords: docker, hello world, examples |
|
| 3 |
+:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples |
|
| 4 | 4 |
|
| 5 | 5 |
|
| 6 | 6 |
|
| ... | ... |
@@ -16,6 +16,7 @@ Contents: |
| 16 | 16 |
hello_world |
| 17 | 17 |
hello_world_daemon |
| 18 | 18 |
python_web_app |
| 19 |
+ nodejs_web_app |
|
| 19 | 20 |
running_redis_service |
| 20 | 21 |
running_ssh_service |
| 21 | 22 |
couchdb_data_volumes |
| 22 | 23 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,236 @@ |
| 0 |
+:title: Running a Node.js app on CentOS |
|
| 1 |
+:description: Installing and running a Node.js app on CentOS |
|
| 2 |
+:keywords: docker, example, package installation, node, centos |
|
| 3 |
+ |
|
| 4 |
+.. _nodejs_web_app: |
|
| 5 |
+ |
|
| 6 |
+Node.js Web App |
|
| 7 |
+=============== |
|
| 8 |
+ |
|
| 9 |
+.. include:: example_header.inc |
|
| 10 |
+ |
|
| 11 |
+The goal of this example is to show you how you can build your own docker images |
|
| 12 |
+from a parent image using a ``Dockerfile`` . We will do that by making a simple |
|
| 13 |
+Node.js hello world web application running on CentOS. You can get the full |
|
| 14 |
+source code at https://github.com/gasi/docker-node-hello. |
|
| 15 |
+ |
|
| 16 |
+Create Node.js app |
|
| 17 |
+ |
|
| 18 |
+First, create a ``package.json`` file that describes your app and its |
|
| 19 |
+dependencies: |
|
| 20 |
+ |
|
| 21 |
+.. code-block:: json |
|
| 22 |
+ |
|
| 23 |
+ {
|
|
| 24 |
+ "name": "docker-centos-hello", |
|
| 25 |
+ "private": true, |
|
| 26 |
+ "version": "0.0.1", |
|
| 27 |
+ "description": "Node.js Hello World app on CentOS using docker", |
|
| 28 |
+ "author": "Daniel Gasienica <daniel@gasienica.ch>", |
|
| 29 |
+ "dependencies": {
|
|
| 30 |
+ "express": "3.2.4" |
|
| 31 |
+ } |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+Then, create an ``index.js`` file that defines a web app using the |
|
| 35 |
+`Express.js <http://expressjs.com/>`_ framework: |
|
| 36 |
+ |
|
| 37 |
+.. code-block:: javascript |
|
| 38 |
+ |
|
| 39 |
+ var express = require('express');
|
|
| 40 |
+ |
|
| 41 |
+ // Constants |
|
| 42 |
+ var PORT = 8080; |
|
| 43 |
+ |
|
| 44 |
+ // App |
|
| 45 |
+ var app = express(); |
|
| 46 |
+ app.get('/', function (req, res) {
|
|
| 47 |
+ res.send('Hello World\n');
|
|
| 48 |
+ }); |
|
| 49 |
+ |
|
| 50 |
+ app.listen(PORT) |
|
| 51 |
+ console.log('Running on http://localhost:' + PORT);
|
|
| 52 |
+ |
|
| 53 |
+ |
|
| 54 |
+In the next steps, we’ll look at how you can run this app inside a CentOS |
|
| 55 |
+container using docker. First, you’ll need to build a docker image of your app. |
|
| 56 |
+ |
|
| 57 |
+Creating a ``Dockerfile`` |
|
| 58 |
+ |
|
| 59 |
+Create an empty file called ``Dockerfile``: |
|
| 60 |
+ |
|
| 61 |
+.. code-block:: bash |
|
| 62 |
+ |
|
| 63 |
+ touch Dockerfile |
|
| 64 |
+ |
|
| 65 |
+Open the ``Dockerfile`` in your favorite text editor and add the following line |
|
| 66 |
+that defines the version of docker the image requires to build |
|
| 67 |
+(this example uses docker 0.3.4): |
|
| 68 |
+ |
|
| 69 |
+.. code-block:: bash |
|
| 70 |
+ |
|
| 71 |
+ # DOCKER-VERSION 0.3.4 |
|
| 72 |
+ |
|
| 73 |
+Next, define the parent image you want to use to build your own image on top of. |
|
| 74 |
+Here, we’ll use `CentOS <https://index.docker.io/_/centos/>`_ (tag: ``6.4``) |
|
| 75 |
+available on the `docker index`_: |
|
| 76 |
+ |
|
| 77 |
+.. code-block:: bash |
|
| 78 |
+ |
|
| 79 |
+ FROM centos:6.4 |
|
| 80 |
+ |
|
| 81 |
+Since we’re building a Node.js app, you’ll have to install Node.js as well as |
|
| 82 |
+npm on your CentOS image. Node.js is required to run your app and npm to install |
|
| 83 |
+your app’s dependencies defined in ``package.json``. |
|
| 84 |
+To install the right package for CentOS, we’ll use the instructions from the |
|
| 85 |
+`Node.js wiki`_: |
|
| 86 |
+ |
|
| 87 |
+.. code-block:: bash |
|
| 88 |
+ |
|
| 89 |
+ # Enable EPEL for Node.js |
|
| 90 |
+ RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm |
|
| 91 |
+ # Install Node.js and npm |
|
| 92 |
+ RUN yum install -y npm-1.2.17-5.el6 |
|
| 93 |
+ |
|
| 94 |
+To bundle your app’s source code inside the docker image, use the ``ADD`` |
|
| 95 |
+command: |
|
| 96 |
+ |
|
| 97 |
+.. code-block:: bash |
|
| 98 |
+ |
|
| 99 |
+ # Bundle app source |
|
| 100 |
+ ADD . /src |
|
| 101 |
+ |
|
| 102 |
+Install your app dependencies using npm: |
|
| 103 |
+ |
|
| 104 |
+.. code-block:: bash |
|
| 105 |
+ |
|
| 106 |
+ # Install app dependencies |
|
| 107 |
+ RUN cd /src; npm install |
|
| 108 |
+ |
|
| 109 |
+Your app binds to port ``8080`` so you’ll use the ``EXPOSE`` command to have it |
|
| 110 |
+mapped by the docker daemon: |
|
| 111 |
+ |
|
| 112 |
+.. code-block:: bash |
|
| 113 |
+ |
|
| 114 |
+ EXPOSE 8080 |
|
| 115 |
+ |
|
| 116 |
+Last but not least, define the command to run your app using ``CMD`` which |
|
| 117 |
+defines your runtime, i.e. ``node``, and the path to our app, i.e. |
|
| 118 |
+``src/index.js`` (see the step where we added the source to the container): |
|
| 119 |
+ |
|
| 120 |
+.. code-block:: bash |
|
| 121 |
+ |
|
| 122 |
+ CMD ["node", "/src/index.js"] |
|
| 123 |
+ |
|
| 124 |
+Your ``Dockerfile`` should now look like this: |
|
| 125 |
+ |
|
| 126 |
+.. code-block:: bash |
|
| 127 |
+ |
|
| 128 |
+ |
|
| 129 |
+ # DOCKER-VERSION 0.3.4 |
|
| 130 |
+ FROM centos:6.4 |
|
| 131 |
+ |
|
| 132 |
+ # Enable EPEL for Node.js |
|
| 133 |
+ RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm |
|
| 134 |
+ # Install Node.js and npm |
|
| 135 |
+ RUN yum install -y npm-1.2.17-5.el6 |
|
| 136 |
+ |
|
| 137 |
+ # Bundle app source |
|
| 138 |
+ ADD . /src |
|
| 139 |
+ # Install app dependencies |
|
| 140 |
+ RUN cd /src; npm install |
|
| 141 |
+ |
|
| 142 |
+ EXPOSE 8080 |
|
| 143 |
+ CMD ["node", "/src/index.js"] |
|
| 144 |
+ |
|
| 145 |
+ |
|
| 146 |
+Building your image |
|
| 147 |
+ |
|
| 148 |
+Go to the directory that has your ``Dockerfile`` and run the following command |
|
| 149 |
+to build a docker image. The ``-t`` flag let’s you tag your image so it’s easier |
|
| 150 |
+to find later using the ``docker images`` command: |
|
| 151 |
+ |
|
| 152 |
+.. code-block:: bash |
|
| 153 |
+ |
|
| 154 |
+ docker build -t <your username>/centos-node-hello . |
|
| 155 |
+ |
|
| 156 |
+Your image will now be listed by docker: |
|
| 157 |
+ |
|
| 158 |
+.. code-block:: bash |
|
| 159 |
+ |
|
| 160 |
+ docker images |
|
| 161 |
+ |
|
| 162 |
+ > # Example |
|
| 163 |
+ > REPOSITORY TAG ID CREATED |
|
| 164 |
+ > centos 6.4 539c0211cd76 8 weeks ago |
|
| 165 |
+ > gasi/centos-node-hello latest d64d3505b0d2 2 hours ago |
|
| 166 |
+ |
|
| 167 |
+ |
|
| 168 |
+Run the image |
|
| 169 |
+ |
|
| 170 |
+Running your image with ``-d`` runs the container in detached mode, leaving the |
|
| 171 |
+container running in the background. Run the image you previously built: |
|
| 172 |
+ |
|
| 173 |
+.. code-block:: bash |
|
| 174 |
+ |
|
| 175 |
+ docker run -d <your username>/centos-node-hello |
|
| 176 |
+ |
|
| 177 |
+Print the output of your app: |
|
| 178 |
+ |
|
| 179 |
+.. code-block:: bash |
|
| 180 |
+ |
|
| 181 |
+ # Get container ID |
|
| 182 |
+ docker ps |
|
| 183 |
+ |
|
| 184 |
+ # Print app output |
|
| 185 |
+ docker logs <container id> |
|
| 186 |
+ |
|
| 187 |
+ > # Example |
|
| 188 |
+ > Running on http://localhost:8080 |
|
| 189 |
+ |
|
| 190 |
+ |
|
| 191 |
+Test |
|
| 192 |
+ |
|
| 193 |
+To test your app, get the the port of your app that docker mapped: |
|
| 194 |
+ |
|
| 195 |
+.. code-block:: bash |
|
| 196 |
+ |
|
| 197 |
+ docker ps |
|
| 198 |
+ |
|
| 199 |
+ > # Example |
|
| 200 |
+ > ID IMAGE COMMAND ... PORTS |
|
| 201 |
+ > ecce33b30ebf gasi/centos-node-hello:latest node /src/index.js 49160->8080 |
|
| 202 |
+ |
|
| 203 |
+In the example above, docker mapped the ``8080`` port of the container to |
|
| 204 |
+``49160``. |
|
| 205 |
+ |
|
| 206 |
+Now you can call your app using ``curl`` (install if needed via: |
|
| 207 |
+``sudo apt-get install curl``): |
|
| 208 |
+ |
|
| 209 |
+.. code-block:: bash |
|
| 210 |
+ |
|
| 211 |
+ curl -i localhost:49160 |
|
| 212 |
+ |
|
| 213 |
+ > HTTP/1.1 200 OK |
|
| 214 |
+ > X-Powered-By: Express |
|
| 215 |
+ > Content-Type: text/html; charset=utf-8 |
|
| 216 |
+ > Content-Length: 12 |
|
| 217 |
+ > Date: Sun, 02 Jun 2013 03:53:22 GMT |
|
| 218 |
+ > Connection: keep-alive |
|
| 219 |
+ > |
|
| 220 |
+ > Hello World |
|
| 221 |
+ |
|
| 222 |
+We hope this tutorial helped you get up and running with Node.js and CentOS on |
|
| 223 |
+docker. You can get the full source code at |
|
| 224 |
+https://github.com/gasi/docker-node-hello. |
|
| 225 |
+ |
|
| 226 |
+Continue to :ref:`running_redis_service`. |
|
| 227 |
+ |
|
| 228 |
+ |
|
| 229 |
+.. _Node.js wiki: https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#rhelcentosscientific-linux-6 |
|
| 230 |
+.. _docker index: https://index.docker.io/ |
| ... | ... |
@@ -4,8 +4,8 @@ |
| 4 | 4 |
|
| 5 | 5 |
.. _running_redis_service: |
| 6 | 6 |
|
| 7 |
-Create a redis service |
|
| 8 |
-====================== |
|
| 7 |
+Redis Service |
|
| 8 |
+============= |
|
| 9 | 9 |
|
| 10 | 10 |
.. include:: example_header.inc |
| 11 | 11 |
|
| ... | ... |
@@ -34,7 +34,7 @@ Snapshot the installation |
| 34 | 34 |
|
| 35 | 35 |
.. code-block:: bash |
| 36 | 36 |
|
| 37 |
- docker ps -a # grab the container id (this will be the last one in the list) |
|
| 37 |
+ docker ps -a # grab the container id (this will be the first one in the list) |
|
| 38 | 38 |
docker commit <container_id> <your username>/redis |
| 39 | 39 |
|
| 40 | 40 |
Run the service |
| ... | ... |
@@ -4,8 +4,8 @@ |
| 4 | 4 |
|
| 5 | 5 |
.. _running_ssh_service: |
| 6 | 6 |
|
| 7 |
-Create an ssh daemon service |
|
| 8 |
-============================ |
|
| 7 |
+SSH Daemon Service |
|
| 8 |
+================== |
|
| 9 | 9 |
|
| 10 | 10 |
.. include:: example_header.inc |
| 11 | 11 |
|
| ... | ... |
@@ -20,8 +20,7 @@ minutes and not entirely smooth, but gives you a good idea. |
| 20 | 20 |
<div style="margin-top:10px;"> |
| 21 | 21 |
<iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe> |
| 22 | 22 |
</div> |
| 23 |
- |
|
| 24 |
- |
|
| 23 |
+ |
|
| 25 | 24 |
You can also get this sshd container by using |
| 26 | 25 |
:: |
| 27 | 26 |
|
| ... | ... |
@@ -30,3 +29,49 @@ You can also get this sshd container by using |
| 30 | 30 |
|
| 31 | 31 |
The password is 'screencast' |
| 32 | 32 |
|
| 33 |
+**Video's Transcription:** |
|
| 34 |
+ |
|
| 35 |
+.. code-block:: bash |
|
| 36 |
+ |
|
| 37 |
+ # Hello! We are going to try and install openssh on a container and run it as a servic |
|
| 38 |
+ # let's pull base to get a base ubuntu image. |
|
| 39 |
+ $ docker pull base |
|
| 40 |
+ # I had it so it was quick |
|
| 41 |
+ # now let's connect using -i for interactive and with -t for terminal |
|
| 42 |
+ # we execute /bin/bash to get a prompt. |
|
| 43 |
+ $ docker run -i -t base /bin/bash |
|
| 44 |
+ # now let's commit it |
|
| 45 |
+ # which container was it? |
|
| 46 |
+ $ docker ps -a |more |
|
| 47 |
+ $ docker commit a30a3a2f2b130749995f5902f079dc6ad31ea0621fac595128ec59c6da07feea dhrp/sshd |
|
| 48 |
+ # I gave the name dhrp/sshd for the container |
|
| 49 |
+ # now we can run it again |
|
| 50 |
+ $ docker run -d dhrp/sshd /usr/sbin/sshd -D # D for daemon mode |
|
| 51 |
+ # is it running? |
|
| 52 |
+ $ docker ps |
|
| 53 |
+ # yes! |
|
| 54 |
+ # let's stop it |
|
| 55 |
+ $ docker stop 0ebf7cec294755399d063f4b1627980d4cbff7d999f0bc82b59c300f8536a562 |
|
| 56 |
+ $ docker ps |
|
| 57 |
+ # and reconnect, but now open a port to it |
|
| 58 |
+ $ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D |
|
| 59 |
+ $ docker port b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 22 |
|
| 60 |
+ # it has now given us a port to connect to |
|
| 61 |
+ # we have to connect using a public ip of our host |
|
| 62 |
+ $ hostname |
|
| 63 |
+ $ ifconfig |
|
| 64 |
+ $ ssh root@192.168.33.10 -p 49153 |
|
| 65 |
+ # Ah! forgot to set root passwd |
|
| 66 |
+ $ docker commit b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 dhrp/sshd |
|
| 67 |
+ $ docker ps -a |
|
| 68 |
+ $ docker run -i -t dhrp/sshd /bin/bash |
|
| 69 |
+ $ passwd |
|
| 70 |
+ $ exit |
|
| 71 |
+ $ docker commit 9e863f0ca0af31c8b951048ba87641d67c382d08d655c2e4879c51410e0fedc1 dhrp/sshd |
|
| 72 |
+ $ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D |
|
| 73 |
+ $ docker port a0aaa9558c90cf5c7782648df904a82365ebacce523e4acc085ac1213bfe2206 22 |
|
| 74 |
+ $ ifconfig |
|
| 75 |
+ $ ssh root@192.168.33.10 -p 49154 |
|
| 76 |
+ # Thanks for watching, Thatcher thatcher@dotcloud.com |
|
| 77 |
+ |
|
| 78 |
+ |
| ... | ... |
@@ -19,7 +19,8 @@ Most frequently asked questions. |
| 19 | 19 |
|
| 20 | 20 |
3. **Does Docker run on Mac OS X or Windows?** |
| 21 | 21 |
|
| 22 |
- Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a virtual machine on your box, and get the best of both worlds. Check out the MacOSX_ and Windows_ installation guides. |
|
| 22 |
+ Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a |
|
| 23 |
+ virtual machine on your box, and get the best of both worlds. Check out the :ref:`install_using_vagrant` and :ref:`windows` installation guides. |
|
| 23 | 24 |
|
| 24 | 25 |
4. **How do containers compare to virtual machines?** |
| 25 | 26 |
|
| ... | ... |
@@ -39,10 +40,8 @@ Most frequently asked questions. |
| 39 | 39 |
* `Ask questions on Stackoverflow`_ |
| 40 | 40 |
* `Join the conversation on Twitter`_ |
| 41 | 41 |
|
| 42 |
- .. _Windows: ../installation/windows/ |
|
| 43 |
- .. _MacOSX: ../installation/vagrant/ |
|
| 44 | 42 |
.. _the repo: http://www.github.com/dotcloud/docker |
| 45 |
- .. _IRC\: docker on freenode: irc://chat.freenode.net#docker |
|
| 43 |
+ .. _IRC: docker on freenode: docker on freenode: irc://chat.freenode.net#docker |
|
| 46 | 44 |
.. _Github: http://www.github.com/dotcloud/docker |
| 47 | 45 |
.. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker |
| 48 | 46 |
.. _Join the conversation on Twitter: http://twitter.com/getdocker |
| ... | ... |
@@ -7,8 +7,8 @@ |
| 7 | 7 |
Introduction |
| 8 | 8 |
============ |
| 9 | 9 |
|
| 10 |
-Docker - The Linux container runtime |
|
| 10 |
+Docker -- The Linux container runtime |
|
| 11 |
+------------------------------------- |
|
| 11 | 12 |
|
| 12 | 13 |
Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers. |
| 13 | 14 |
|
| ... | ... |
@@ -92,6 +92,16 @@ have AUFS filesystem support enabled, so we need to install it. |
| 92 | 92 |
sudo apt-get update |
| 93 | 93 |
sudo apt-get install linux-image-extra-`uname -r` |
| 94 | 94 |
|
| 95 |
+**add-apt-repository support** |
|
| 96 |
+ |
|
| 97 |
+Some installations of Ubuntu 13.04 require ``software-properties-common`` to be |
|
| 98 |
+installed before being able to use add-apt-repository. |
|
| 99 |
+ |
|
| 100 |
+.. code-block:: bash |
|
| 101 |
+ |
|
| 102 |
+ sudo apt-get install software-properties-common |
|
| 103 |
+ |
|
| 104 |
+ |
|
| 95 | 105 |
Installation |
| 96 | 106 |
------------ |
| 97 | 107 |
|
| ... | ... |
@@ -125,8 +125,14 @@ curl was installed within the image. |
| 125 | 125 |
.. note:: |
| 126 | 126 |
The path must include the file name. |
| 127 | 127 |
|
| 128 |
-.. note:: |
|
| 129 |
- This instruction has temporarily disabled |
|
| 128 |
+2.8 ADD |
|
| 129 |
+------- |
|
| 130 |
+ |
|
| 131 |
+ ``ADD <src> <dest>`` |
|
| 132 |
+ |
|
| 133 |
+The `ADD` instruction will insert the files from the `<src>` path of the context into `<dest>` path |
|
| 134 |
+of the container. |
|
| 135 |
+The context must be set in order to use this instruction. (see examples) |
|
| 130 | 136 |
|
| 131 | 137 |
3. Dockerfile Examples |
| 132 | 138 |
====================== |
| ... | ... |
@@ -4,8 +4,8 @@ |
| 4 | 4 |
|
| 5 | 5 |
.. _working_with_the_repository: |
| 6 | 6 |
|
| 7 |
-Working with the repository |
|
| 8 |
-============================ |
|
| 7 |
+Working with the Repository |
|
| 8 |
+=========================== |
|
| 9 | 9 |
|
| 10 | 10 |
|
| 11 | 11 |
Top-level repositories and user repositories |
| ... | ... |
@@ -14,9 +14,9 @@ Top-level repositories and user repositories |
| 14 | 14 |
Generally, there are two types of repositories: Top-level repositories which are controlled by the people behind |
| 15 | 15 |
Docker, and user repositories. |
| 16 | 16 |
|
| 17 |
-* Top-level repositories can easily be recognized by not having a / (slash) in their name. These repositories can |
|
| 17 |
+* Top-level repositories can easily be recognized by not having a ``/`` (slash) in their name. These repositories can |
|
| 18 | 18 |
generally be trusted. |
| 19 |
-* User repositories always come in the form of <username>/<repo_name>. This is what your published images will look like. |
|
| 19 |
+* User repositories always come in the form of ``<username>/<repo_name>``. This is what your published images will look like. |
|
| 20 | 20 |
* User images are not checked, it is therefore up to you whether or not you trust the creator of this image. |
| 21 | 21 |
|
| 22 | 22 |
|
| ... | ... |
@@ -62,7 +62,7 @@ |
| 62 | 62 |
</div> |
| 63 | 63 |
|
| 64 | 64 |
<div class="container"> |
| 65 |
- <div class="alert alert-info"> |
|
| 65 |
+ <div class="alert alert-info" style="margin-bottom: 0;"> |
|
| 66 | 66 |
<strong>Docker is still under heavy development.</strong> It should not yet be used in production. Check <a href="http://github.com/dotcloud/docker">the repo</a> for recent progress. |
| 67 | 67 |
</div> |
| 68 | 68 |
<div class="row"> |
| ... | ... |
@@ -133,13 +133,13 @@ |
| 133 | 133 |
</section> |
| 134 | 134 |
|
| 135 | 135 |
<section class="contentblock"> |
| 136 |
- <h2>More resources</h2> |
|
| 137 |
- <ul> |
|
| 138 |
- <li><a href="irc://chat.freenode.net#docker">IRC: docker on freenode</a></li> |
|
| 139 |
- <li><a href="http://www.github.com/dotcloud/docker">Github</a></li> |
|
| 140 |
- <li><a href="http://stackoverflow.com/tags/docker/">Ask questions on Stackoverflow</a></li> |
|
| 141 |
- <li><a href="http://twitter.com/getdocker/">Join the conversation on Twitter</a></li> |
|
| 142 |
- </ul> |
|
| 136 |
+ <h2>Questions? Want to get in touch?</h2> |
|
| 137 |
+ <p>There are several ways to get in touch:</p> |
|
| 138 |
+ <p><strong>Join the discussion on IRC.</strong> We can be found in the <a href="irc://chat.freenode.net#docker">#docker</a> channel on chat.freenode.net</p> |
|
| 139 |
+ <p><strong>Discussions</strong> happen on our google group: <a href="https://groups.google.com/d/forum/docker-club">docker-club at googlegroups.com</a></p> |
|
| 140 |
+ <p>All our <strong>development and decisions</strong> are made out in the open on Github <a href="http://www.github.com/dotcloud/docker">github.com/dotcloud/docker</a></p> |
|
| 141 |
+ <p><strong>Get help on using Docker</strong> by asking on <a href="http://stackoverflow.com/tags/docker/">Stackoverflow</a></p> |
|
| 142 |
+ <p>And of course, <strong>tweet</strong> your tweets to <a href="http://twitter.com/getdocker/">twitter.com/getdocker</a></p> |
|
| 143 | 143 |
</section> |
| 144 | 144 |
|
| 145 | 145 |
|
| ... | ... |
@@ -270,7 +270,7 @@ |
| 270 | 270 |
<li>Filesystem isolation: each process container runs in a completely separate root filesystem.</li> |
| 271 | 271 |
<li>Resource isolation: system resources like cpu and memory can be allocated differently to each process container, using cgroups.</li> |
| 272 | 272 |
<li>Network isolation: each process container runs in its own network namespace, with a virtual interface and IP address of its own.</li> |
| 273 |
- <li>Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremeley fast, memory-cheap and disk-cheap.</li> |
|
| 273 |
+ <li>Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremely fast, memory-cheap and disk-cheap.</li> |
|
| 274 | 274 |
<li>Logging: the standard streams (stdout/stderr/stdin) of each process container is collected and logged for real-time or batch retrieval.</li> |
| 275 | 275 |
<li>Change management: changes to a container's filesystem can be committed into a new image and re-used to create more containers. No templating or manual configuration required.</li> |
| 276 | 276 |
<li>Interactive shell: docker can allocate a pseudo-tty and attach to the standard input of any container, for example to run a throwaway interactive shell.</li> |
| ... | ... |
@@ -86,8 +86,8 @@ func (graph *Graph) Get(name string) (*Image, error) {
|
| 86 | 86 |
if err != nil {
|
| 87 | 87 |
return nil, err |
| 88 | 88 |
} |
| 89 |
- if img.Id != id {
|
|
| 90 |
- return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.Id)
|
|
| 89 |
+ if img.ID != id {
|
|
| 90 |
+ return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
|
|
| 91 | 91 |
} |
| 92 | 92 |
img.graph = graph |
| 93 | 93 |
if img.Size == 0 {
|
| ... | ... |
@@ -101,8 +101,8 @@ func (graph *Graph) Get(name string) (*Image, error) {
|
| 101 | 101 |
} |
| 102 | 102 |
graph.lockSumMap.Lock() |
| 103 | 103 |
defer graph.lockSumMap.Unlock() |
| 104 |
- if _, exists := graph.checksumLock[img.Id]; !exists {
|
|
| 105 |
- graph.checksumLock[img.Id] = &sync.Mutex{}
|
|
| 104 |
+ if _, exists := graph.checksumLock[img.ID]; !exists {
|
|
| 105 |
+ graph.checksumLock[img.ID] = &sync.Mutex{}
|
|
| 106 | 106 |
} |
| 107 | 107 |
return img, nil |
| 108 | 108 |
} |
| ... | ... |
@@ -110,16 +110,17 @@ func (graph *Graph) Get(name string) (*Image, error) {
|
| 110 | 110 |
// Create creates a new image and registers it in the graph. |
| 111 | 111 |
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
|
| 112 | 112 |
img := &Image{
|
| 113 |
- Id: GenerateId(), |
|
| 113 |
+ ID: GenerateID(), |
|
| 114 | 114 |
Comment: comment, |
| 115 | 115 |
Created: time.Now(), |
| 116 | 116 |
DockerVersion: VERSION, |
| 117 | 117 |
Author: author, |
| 118 | 118 |
Config: config, |
| 119 |
+ Architecture: "x86_64", |
|
| 119 | 120 |
} |
| 120 | 121 |
if container != nil {
|
| 121 | 122 |
img.Parent = container.Image |
| 122 |
- img.Container = container.Id |
|
| 123 |
+ img.Container = container.ID |
|
| 123 | 124 |
img.ContainerConfig = *container.Config |
| 124 | 125 |
} |
| 125 | 126 |
if err := graph.Register(layerData, layerData != nil, img); err != nil {
|
| ... | ... |
@@ -132,12 +133,12 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut |
| 132 | 132 |
// Register imports a pre-existing image into the graph. |
| 133 | 133 |
// FIXME: pass img as first argument |
| 134 | 134 |
func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
|
| 135 |
- if err := ValidateId(img.Id); err != nil {
|
|
| 135 |
+ if err := ValidateID(img.ID); err != nil {
|
|
| 136 | 136 |
return err |
| 137 | 137 |
} |
| 138 | 138 |
// (This is a convenience to save time. Race conditions are taken care of by os.Rename) |
| 139 |
- if graph.Exists(img.Id) {
|
|
| 140 |
- return fmt.Errorf("Image %s already exists", img.Id)
|
|
| 139 |
+ if graph.Exists(img.ID) {
|
|
| 140 |
+ return fmt.Errorf("Image %s already exists", img.ID)
|
|
| 141 | 141 |
} |
| 142 | 142 |
tmp, err := graph.Mktemp("")
|
| 143 | 143 |
defer os.RemoveAll(tmp) |
| ... | ... |
@@ -148,12 +149,12 @@ func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
|
| 148 | 148 |
return err |
| 149 | 149 |
} |
| 150 | 150 |
// Commit |
| 151 |
- if err := os.Rename(tmp, graph.imageRoot(img.Id)); err != nil {
|
|
| 151 |
+ if err := os.Rename(tmp, graph.imageRoot(img.ID)); err != nil {
|
|
| 152 | 152 |
return err |
| 153 | 153 |
} |
| 154 | 154 |
img.graph = graph |
| 155 |
- graph.idIndex.Add(img.Id) |
|
| 156 |
- graph.checksumLock[img.Id] = &sync.Mutex{}
|
|
| 155 |
+ graph.idIndex.Add(img.ID) |
|
| 156 |
+ graph.checksumLock[img.ID] = &sync.Mutex{}
|
|
| 157 | 157 |
return nil |
| 158 | 158 |
} |
| 159 | 159 |
|
| ... | ... |
@@ -174,13 +175,14 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output |
| 174 | 174 |
if err != nil {
|
| 175 | 175 |
return nil, err |
| 176 | 176 |
} |
| 177 |
- return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)", false), tmp.Root) |
|
| 177 |
+ sf := utils.NewStreamFormatter(false) |
|
| 178 |
+ return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, sf.FormatProgress("Buffering to disk", "%v/%v (%v)"), sf), tmp.Root)
|
|
| 178 | 179 |
} |
| 179 | 180 |
|
| 180 | 181 |
// Mktemp creates a temporary sub-directory inside the graph's filesystem. |
| 181 | 182 |
func (graph *Graph) Mktemp(id string) (string, error) {
|
| 182 | 183 |
if id == "" {
|
| 183 |
- id = GenerateId() |
|
| 184 |
+ id = GenerateID() |
|
| 184 | 185 |
} |
| 185 | 186 |
tmp, err := graph.tmp() |
| 186 | 187 |
if err != nil {
|
| ... | ... |
@@ -237,7 +239,7 @@ func (graph *Graph) Map() (map[string]*Image, error) {
|
| 237 | 237 |
} |
| 238 | 238 |
images := make(map[string]*Image, len(all)) |
| 239 | 239 |
for _, image := range all {
|
| 240 |
- images[image.Id] = image |
|
| 240 |
+ images[image.ID] = image |
|
| 241 | 241 |
} |
| 242 | 242 |
return images, nil |
| 243 | 243 |
} |
| ... | ... |
@@ -280,10 +282,10 @@ func (graph *Graph) ByParent() (map[string][]*Image, error) {
|
| 280 | 280 |
if err != nil {
|
| 281 | 281 |
return |
| 282 | 282 |
} |
| 283 |
- if children, exists := byParent[parent.Id]; exists {
|
|
| 284 |
- byParent[parent.Id] = []*Image{image}
|
|
| 283 |
+ if children, exists := byParent[parent.ID]; exists {
|
|
| 284 |
+ byParent[parent.ID] = []*Image{image}
|
|
| 285 | 285 |
} else {
|
| 286 |
- byParent[parent.Id] = append(children, image) |
|
| 286 |
+ byParent[parent.ID] = append(children, image) |
|
| 287 | 287 |
} |
| 288 | 288 |
}) |
| 289 | 289 |
return byParent, err |
| ... | ... |
@@ -300,8 +302,8 @@ func (graph *Graph) Heads() (map[string]*Image, error) {
|
| 300 | 300 |
err = graph.WalkAll(func(image *Image) {
|
| 301 | 301 |
// If it's not in the byParent lookup table, then |
| 302 | 302 |
// it's not a parent -> so it's a head! |
| 303 |
- if _, exists := byParent[image.Id]; !exists {
|
|
| 304 |
- heads[image.Id] = image |
|
| 303 |
+ if _, exists := byParent[image.ID]; !exists {
|
|
| 304 |
+ heads[image.ID] = image |
|
| 305 | 305 |
} |
| 306 | 306 |
}) |
| 307 | 307 |
return heads, err |
| ... | ... |
@@ -324,11 +326,11 @@ func (graph *Graph) getStoredChecksums() (map[string]string, error) {
|
| 324 | 324 |
} |
| 325 | 325 |
|
| 326 | 326 |
func (graph *Graph) storeChecksums(checksums map[string]string) error {
|
| 327 |
- checksumJson, err := json.Marshal(checksums) |
|
| 327 |
+ checksumJSON, err := json.Marshal(checksums) |
|
| 328 | 328 |
if err != nil {
|
| 329 | 329 |
return err |
| 330 | 330 |
} |
| 331 |
- if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJson, 0600); err != nil {
|
|
| 331 |
+ if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJSON, 0600); err != nil {
|
|
| 332 | 332 |
return err |
| 333 | 333 |
} |
| 334 | 334 |
return nil |
| ... | ... |
@@ -34,14 +34,14 @@ func TestInterruptedRegister(t *testing.T) {
|
| 34 | 34 |
defer os.RemoveAll(graph.Root) |
| 35 | 35 |
badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data |
| 36 | 36 |
image := &Image{
|
| 37 |
- Id: GenerateId(), |
|
| 37 |
+ ID: GenerateID(), |
|
| 38 | 38 |
Comment: "testing", |
| 39 | 39 |
Created: time.Now(), |
| 40 | 40 |
} |
| 41 | 41 |
go graph.Register(badArchive, false, image) |
| 42 | 42 |
time.Sleep(200 * time.Millisecond) |
| 43 | 43 |
w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling)
|
| 44 |
- if _, err := graph.Get(image.Id); err == nil {
|
|
| 44 |
+ if _, err := graph.Get(image.ID); err == nil {
|
|
| 45 | 45 |
t.Fatal("Image should not exist after Register is interrupted")
|
| 46 | 46 |
} |
| 47 | 47 |
// Registering the same image again should succeed if the first register was interrupted |
| ... | ... |
@@ -67,7 +67,7 @@ func TestGraphCreate(t *testing.T) {
|
| 67 | 67 |
if err != nil {
|
| 68 | 68 |
t.Fatal(err) |
| 69 | 69 |
} |
| 70 |
- if err := ValidateId(image.Id); err != nil {
|
|
| 70 |
+ if err := ValidateID(image.ID); err != nil {
|
|
| 71 | 71 |
t.Fatal(err) |
| 72 | 72 |
} |
| 73 | 73 |
if image.Comment != "Testing" {
|
| ... | ... |
@@ -91,7 +91,7 @@ func TestRegister(t *testing.T) {
|
| 91 | 91 |
t.Fatal(err) |
| 92 | 92 |
} |
| 93 | 93 |
image := &Image{
|
| 94 |
- Id: GenerateId(), |
|
| 94 |
+ ID: GenerateID(), |
|
| 95 | 95 |
Comment: "testing", |
| 96 | 96 |
Created: time.Now(), |
| 97 | 97 |
} |
| ... | ... |
@@ -104,11 +104,11 @@ func TestRegister(t *testing.T) {
|
| 104 | 104 |
} else if l := len(images); l != 1 {
|
| 105 | 105 |
t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
|
| 106 | 106 |
} |
| 107 |
- if resultImg, err := graph.Get(image.Id); err != nil {
|
|
| 107 |
+ if resultImg, err := graph.Get(image.ID); err != nil {
|
|
| 108 | 108 |
t.Fatal(err) |
| 109 | 109 |
} else {
|
| 110 |
- if resultImg.Id != image.Id {
|
|
| 111 |
- t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.Id, resultImg.Id)
|
|
| 110 |
+ if resultImg.ID != image.ID {
|
|
| 111 |
+ t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.ID, resultImg.ID)
|
|
| 112 | 112 |
} |
| 113 | 113 |
if resultImg.Comment != image.Comment {
|
| 114 | 114 |
t.Fatalf("Wrong image comment. Should be '%s', not '%s'", image.Comment, resultImg.Comment)
|
| ... | ... |
@@ -156,7 +156,7 @@ func TestDeletePrefix(t *testing.T) {
|
| 156 | 156 |
graph := tempGraph(t) |
| 157 | 157 |
defer os.RemoveAll(graph.Root) |
| 158 | 158 |
img := createTestImage(graph, t) |
| 159 |
- if err := graph.Delete(utils.TruncateId(img.Id)); err != nil {
|
|
| 159 |
+ if err := graph.Delete(utils.TruncateID(img.ID)); err != nil {
|
|
| 160 | 160 |
t.Fatal(err) |
| 161 | 161 |
} |
| 162 | 162 |
assertNImages(graph, t, 0) |
| ... | ... |
@@ -187,7 +187,7 @@ func TestDelete(t *testing.T) {
|
| 187 | 187 |
t.Fatal(err) |
| 188 | 188 |
} |
| 189 | 189 |
assertNImages(graph, t, 1) |
| 190 |
- if err := graph.Delete(img.Id); err != nil {
|
|
| 190 |
+ if err := graph.Delete(img.ID); err != nil {
|
|
| 191 | 191 |
t.Fatal(err) |
| 192 | 192 |
} |
| 193 | 193 |
assertNImages(graph, t, 0) |
| ... | ... |
@@ -201,7 +201,7 @@ func TestDelete(t *testing.T) {
|
| 201 | 201 |
t.Fatal(err) |
| 202 | 202 |
} |
| 203 | 203 |
assertNImages(graph, t, 2) |
| 204 |
- if err := graph.Delete(img1.Id); err != nil {
|
|
| 204 |
+ if err := graph.Delete(img1.ID); err != nil {
|
|
| 205 | 205 |
t.Fatal(err) |
| 206 | 206 |
} |
| 207 | 207 |
assertNImages(graph, t, 1) |
| ... | ... |
@@ -216,7 +216,7 @@ func TestDelete(t *testing.T) {
|
| 216 | 216 |
if err := graph.Register(archive, false, img1); err != nil {
|
| 217 | 217 |
t.Fatal(err) |
| 218 | 218 |
} |
| 219 |
- if err := graph.Delete(img1.Id); err != nil {
|
|
| 219 |
+ if err := graph.Delete(img1.ID); err != nil {
|
|
| 220 | 220 |
t.Fatal(err) |
| 221 | 221 |
} |
| 222 | 222 |
assertNImages(graph, t, 1) |
| ... | ... |
@@ -1,23 +1,31 @@ |
| 1 | 1 |
# This will build a container capable of producing an official binary build of docker and |
| 2 | 2 |
# uploading it to S3 |
| 3 |
+from ubuntu:12.04 |
|
| 3 | 4 |
maintainer Solomon Hykes <solomon@dotcloud.com> |
| 4 |
-from ubuntu:12.10 |
|
| 5 |
+# Workaround the upstart issue |
|
| 6 |
+run dpkg-divert --local --rename --add /sbin/initctl |
|
| 7 |
+run ln -s /bin/true /sbin/initctl |
|
| 8 |
+# Enable universe and gophers PPA |
|
| 9 |
+run DEBIAN_FRONTEND=noninteractive apt-get install -y -q python-software-properties |
|
| 10 |
+run add-apt-repository "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) universe" |
|
| 11 |
+run add-apt-repository -y ppa:gophers/go/ubuntu |
|
| 5 | 12 |
run apt-get update |
| 13 |
+# Packages required to checkout, build and upload docker |
|
| 6 | 14 |
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd |
| 7 | 15 |
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl |
| 8 |
-# Packages required to checkout and build docker |
|
| 9 | 16 |
run curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.linux-amd64.tar.gz |
| 10 | 17 |
run tar -C /usr/local -xzf /go.tar.gz |
| 11 |
-run echo "export PATH=$PATH:/usr/local/go/bin" > /.bashrc |
|
| 12 |
-run echo "export PATH=$PATH:/usr/local/go/bin" > /.bash_profile |
|
| 18 |
+run echo "export PATH=/usr/local/go/bin:$PATH" > /.bashrc |
|
| 19 |
+run echo "export PATH=/usr/local/go/bin:$PATH" > /.bash_profile |
|
| 13 | 20 |
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git |
| 14 | 21 |
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q build-essential |
| 15 | 22 |
# Packages required to build an ubuntu package |
| 23 |
+run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang-stable |
|
| 16 | 24 |
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q debhelper |
| 17 | 25 |
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q autotools-dev |
| 18 |
-copy fake_initctl /usr/local/bin/initctl |
|
| 19 | 26 |
run apt-get install -y -q devscripts |
| 20 |
-add . /src |
|
| 27 |
+# Copy dockerbuilder files into the container |
|
| 28 |
+add . /src |
|
| 21 | 29 |
run cp /src/dockerbuilder /usr/local/bin/ && chmod +x /usr/local/bin/dockerbuilder |
| 22 | 30 |
run cp /src/s3cfg /.s3cfg |
| 23 | 31 |
cmd ["dockerbuilder"] |
| ... | ... |
@@ -2,7 +2,7 @@ |
| 2 | 2 |
set -x |
| 3 | 3 |
set -e |
| 4 | 4 |
|
| 5 |
-export PATH=$PATH:/usr/local/go/bin |
|
| 5 |
+export PATH=/usr/local/go/bin:$PATH |
|
| 6 | 6 |
|
| 7 | 7 |
PACKAGE=github.com/dotcloud/docker |
| 8 | 8 |
|
| ... | ... |
@@ -36,5 +36,6 @@ else |
| 36 | 36 |
fi |
| 37 | 37 |
|
| 38 | 38 |
if [ -z "$NO_UBUNTU" ]; then |
| 39 |
+ export PATH=`echo $PATH | sed 's#/usr/local/go/bin:##g'` |
|
| 39 | 40 |
(cd packaging/ubuntu && make ubuntu) |
| 40 | 41 |
fi |
| 0 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,5 @@ |
| 0 |
+# Docker project infrastructure |
|
| 1 |
+ |
|
| 2 |
+This directory holds all information about the technical infrastructure of the docker project; servers, dns, email, and all the corresponding tools and configuration. |
|
| 3 |
+ |
|
| 4 |
+Obviously credentials should not be stored in this repo, but how to obtain and use them should be documented here. |
| ... | ... |
@@ -19,7 +19,7 @@ import ( |
| 19 | 19 |
) |
| 20 | 20 |
|
| 21 | 21 |
type Image struct {
|
| 22 |
- Id string `json:"id"` |
|
| 22 |
+ ID string `json:"id"` |
|
| 23 | 23 |
Parent string `json:"parent,omitempty"` |
| 24 | 24 |
Comment string `json:"comment,omitempty"` |
| 25 | 25 |
Created time.Time `json:"created"` |
| ... | ... |
@@ -28,6 +28,7 @@ type Image struct {
|
| 28 | 28 |
DockerVersion string `json:"docker_version,omitempty"` |
| 29 | 29 |
Author string `json:"author,omitempty"` |
| 30 | 30 |
Config *Config `json:"config,omitempty"` |
| 31 |
+ Architecture string `json:"architecture,omitempty"` |
|
| 31 | 32 |
graph *Graph |
| 32 | 33 |
Size int64 |
| 33 | 34 |
ParentSize int64 |
| ... | ... |
@@ -44,18 +45,17 @@ func LoadImage(root string) (*Image, error) {
|
| 44 | 44 |
if err := json.Unmarshal(jsonData, img); err != nil {
|
| 45 | 45 |
return nil, err |
| 46 | 46 |
} |
| 47 |
- if err := ValidateId(img.Id); err != nil {
|
|
| 47 |
+ if err := ValidateID(img.ID); err != nil {
|
|
| 48 | 48 |
return nil, err |
| 49 | 49 |
} |
| 50 | 50 |
// Check that the filesystem layer exists |
| 51 | 51 |
if stat, err := os.Stat(layerPath(root)); err != nil {
|
| 52 | 52 |
if os.IsNotExist(err) {
|
| 53 |
- return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.Id)
|
|
| 54 |
- } else {
|
|
| 55 |
- return nil, err |
|
| 53 |
+ return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
|
|
| 56 | 54 |
} |
| 55 |
+ return nil, err |
|
| 57 | 56 |
} else if !stat.IsDir() {
|
| 58 |
- return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.Id, layerPath(root))
|
|
| 57 |
+ return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
|
|
| 59 | 58 |
} |
| 60 | 59 |
return img, nil |
| 61 | 60 |
} |
| ... | ... |
@@ -63,7 +63,7 @@ func LoadImage(root string) (*Image, error) {
|
| 63 | 63 |
func StoreImage(img *Image, layerData Archive, root string, store bool) error {
|
| 64 | 64 |
// Check that root doesn't already exist |
| 65 | 65 |
if _, err := os.Stat(root); err == nil {
|
| 66 |
- return fmt.Errorf("Image %s already exists", img.Id)
|
|
| 66 |
+ return fmt.Errorf("Image %s already exists", img.ID)
|
|
| 67 | 67 |
} else if !os.IsNotExist(err) {
|
| 68 | 68 |
return err |
| 69 | 69 |
} |
| ... | ... |
@@ -195,11 +195,11 @@ func (image *Image) Changes(rw string) ([]Change, error) {
|
| 195 | 195 |
return Changes(layers, rw) |
| 196 | 196 |
} |
| 197 | 197 |
|
| 198 |
-func (image *Image) ShortId() string {
|
|
| 199 |
- return utils.TruncateId(image.Id) |
|
| 198 |
+func (image *Image) ShortID() string {
|
|
| 199 |
+ return utils.TruncateID(image.ID) |
|
| 200 | 200 |
} |
| 201 | 201 |
|
| 202 |
-func ValidateId(id string) error {
|
|
| 202 |
+func ValidateID(id string) error {
|
|
| 203 | 203 |
if id == "" {
|
| 204 | 204 |
return fmt.Errorf("Image id can't be empty")
|
| 205 | 205 |
} |
| ... | ... |
@@ -209,7 +209,7 @@ func ValidateId(id string) error {
|
| 209 | 209 |
return nil |
| 210 | 210 |
} |
| 211 | 211 |
|
| 212 |
-func GenerateId() string {
|
|
| 212 |
+func GenerateID() string {
|
|
| 213 | 213 |
id := make([]byte, 32) |
| 214 | 214 |
_, err := io.ReadFull(rand.Reader, id) |
| 215 | 215 |
if err != nil {
|
| ... | ... |
@@ -255,7 +255,7 @@ func (img *Image) layers() ([]string, error) {
|
| 255 | 255 |
return nil, e |
| 256 | 256 |
} |
| 257 | 257 |
if len(list) == 0 {
|
| 258 |
- return nil, fmt.Errorf("No layer found for image %s\n", img.Id)
|
|
| 258 |
+ return nil, fmt.Errorf("No layer found for image %s\n", img.ID)
|
|
| 259 | 259 |
} |
| 260 | 260 |
return list, nil |
| 261 | 261 |
} |
| ... | ... |
@@ -290,7 +290,7 @@ func (img *Image) root() (string, error) {
|
| 290 | 290 |
if img.graph == nil {
|
| 291 | 291 |
return "", fmt.Errorf("Can't lookup root of unregistered image")
|
| 292 | 292 |
} |
| 293 |
- return img.graph.imageRoot(img.Id), nil |
|
| 293 |
+ return img.graph.imageRoot(img.ID), nil |
|
| 294 | 294 |
} |
| 295 | 295 |
|
| 296 | 296 |
// Return the path of an image's layer |
| ... | ... |
@@ -303,8 +303,8 @@ func (img *Image) layer() (string, error) {
|
| 303 | 303 |
} |
| 304 | 304 |
|
| 305 | 305 |
func (img *Image) Checksum() (string, error) {
|
| 306 |
- img.graph.checksumLock[img.Id].Lock() |
|
| 307 |
- defer img.graph.checksumLock[img.Id].Unlock() |
|
| 306 |
+ img.graph.checksumLock[img.ID].Lock() |
|
| 307 |
+ defer img.graph.checksumLock[img.ID].Unlock() |
|
| 308 | 308 |
|
| 309 | 309 |
root, err := img.root() |
| 310 | 310 |
if err != nil {
|
| ... | ... |
@@ -315,7 +315,7 @@ func (img *Image) Checksum() (string, error) {
|
| 315 | 315 |
if err != nil {
|
| 316 | 316 |
return "", err |
| 317 | 317 |
} |
| 318 |
- if checksum, ok := checksums[img.Id]; ok {
|
|
| 318 |
+ if checksum, ok := checksums[img.ID]; ok {
|
|
| 319 | 319 |
return checksum, nil |
| 320 | 320 |
} |
| 321 | 321 |
|
| ... | ... |
@@ -366,7 +366,7 @@ func (img *Image) Checksum() (string, error) {
|
| 366 | 366 |
return "", err |
| 367 | 367 |
} |
| 368 | 368 |
|
| 369 |
- checksums[img.Id] = hash |
|
| 369 |
+ checksums[img.ID] = hash |
|
| 370 | 370 |
|
| 371 | 371 |
// Dump the checksums to disc |
| 372 | 372 |
if err := img.graph.storeChecksums(checksums); err != nil {
|
| ... | ... |
@@ -386,7 +386,7 @@ func (img *Image) getVirtualSize(size int64) int64 {
|
| 386 | 386 |
} |
| 387 | 387 |
|
| 388 | 388 |
// Build an Image object from raw json data |
| 389 |
-func NewImgJson(src []byte) (*Image, error) {
|
|
| 389 |
+func NewImgJSON(src []byte) (*Image, error) {
|
|
| 390 | 390 |
ret := &Image{}
|
| 391 | 391 |
|
| 392 | 392 |
utils.Debugf("Json string: {%s}\n", src)
|
| ... | ... |
@@ -19,7 +19,7 @@ lxc.network.flags = up |
| 19 | 19 |
lxc.network.link = {{.NetworkSettings.Bridge}}
|
| 20 | 20 |
lxc.network.name = eth0 |
| 21 | 21 |
lxc.network.mtu = 1500 |
| 22 |
-lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}}
|
|
| 22 |
+lxc.network.ipv4 = {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}
|
|
| 23 | 23 |
|
| 24 | 24 |
# root filesystem |
| 25 | 25 |
{{$ROOTFS := .RootfsPath}}
|
| ... | ... |
@@ -52,7 +52,7 @@ func ipToInt(ip net.IP) int32 {
|
| 52 | 52 |
} |
| 53 | 53 |
|
| 54 | 54 |
// Converts 32 bit integer into a 4 bytes IP address |
| 55 |
-func intToIp(n int32) net.IP {
|
|
| 55 |
+func intToIP(n int32) net.IP {
|
|
| 56 | 56 |
b := make([]byte, 4) |
| 57 | 57 |
binary.BigEndian.PutUint32(b, uint32(n)) |
| 58 | 58 |
return net.IP(b) |
| ... | ... |
@@ -132,9 +132,8 @@ func CreateBridgeIface(ifaceName string) error {
|
| 132 | 132 |
} |
| 133 | 133 |
if ifaceAddr == "" {
|
| 134 | 134 |
return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName)
|
| 135 |
- } else {
|
|
| 136 |
- utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
|
|
| 137 | 135 |
} |
| 136 |
+ utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
|
|
| 138 | 137 |
|
| 139 | 138 |
if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil {
|
| 140 | 139 |
return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output)
|
| ... | ... |
@@ -258,7 +257,7 @@ func proxy(listener net.Listener, proto, address string) error {
|
| 258 | 258 |
utils.Debugf("Connected to backend, splicing")
|
| 259 | 259 |
splice(src, dst) |
| 260 | 260 |
} |
| 261 |
- return nil |
|
| 261 |
+ panic("Unreachable")
|
|
| 262 | 262 |
} |
| 263 | 263 |
|
| 264 | 264 |
func halfSplice(dst, src net.Conn) error {
|
| ... | ... |
@@ -398,7 +397,7 @@ func (alloc *IPAllocator) run() {
|
| 398 | 398 |
} |
| 399 | 399 |
} |
| 400 | 400 |
|
| 401 |
- ip := allocatedIP{ip: intToIp(newNum)}
|
|
| 401 |
+ ip := allocatedIP{ip: intToIP(newNum)}
|
|
| 402 | 402 |
if inUse {
|
| 403 | 403 |
ip.err = errors.New("No unallocated IP available")
|
| 404 | 404 |
} |
| ... | ... |
@@ -465,11 +464,11 @@ func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
|
| 465 | 465 |
return nil, err |
| 466 | 466 |
} |
| 467 | 467 |
// Allocate a random port if Frontend==0 |
| 468 |
- if extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend); err != nil {
|
|
| 468 |
+ extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend) |
|
| 469 |
+ if err != nil {
|
|
| 469 | 470 |
return nil, err |
| 470 |
- } else {
|
|
| 471 |
- nat.Frontend = extPort |
|
| 472 | 471 |
} |
| 472 |
+ nat.Frontend = extPort |
|
| 473 | 473 |
if err := iface.manager.portMapper.Map(nat.Frontend, net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}); err != nil {
|
| 474 | 474 |
iface.manager.portAllocator.Release(nat.Frontend) |
| 475 | 475 |
return nil, err |
| ... | ... |
@@ -1,3 +1,14 @@ |
| 1 |
+lxc-docker (0.3.4-1) UNRELEASED; urgency=low |
|
| 2 |
+ - Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile |
|
| 3 |
+ - Builder: 'docker build -t FOO' applies the tag FOO to the newly built container. |
|
| 4 |
+ - Runtime: interactive TTYs correctly handle window resize |
|
| 5 |
+ - Runtime: fix how configuration is merged between layers |
|
| 6 |
+ - Remote API: split stdout and stderr on 'docker run' |
|
| 7 |
+ - Remote API: optionally listen on a different IP and port (use at your own risk) |
|
| 8 |
+ - Documentation: improved install instructions. |
|
| 9 |
+ |
|
| 10 |
+ -- dotCloud <ops@dotcloud.com> Thu, 30 May 2013 00:00:00 -0700 |
|
| 11 |
+ |
|
| 1 | 12 |
lxc-docker (0.3.2-1) UNRELEASED; urgency=low |
| 2 | 13 |
- Runtime: Store the actual archive on commit |
| 3 | 14 |
- Registry: Improve the checksum process |
| ... | ... |
@@ -1,3 +1,22 @@ |
| 1 |
+lxc-docker (0.4.0-1) precise; urgency=low |
|
| 2 |
+ - Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile |
|
| 3 |
+ - Introducing Remote API: control Docker programmatically using a simple HTTP/json API |
|
| 4 |
+ - Runtime: various reliability and usability improvements |
|
| 5 |
+ |
|
| 6 |
+ -- dotCloud <ops@dotcloud.com> Mon, 03 Jun 2013 00:00:00 -0700 |
|
| 7 |
+ |
|
| 8 |
+lxc-docker (0.3.4-1) precise; urgency=low |
|
| 9 |
+ - Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile |
|
| 10 |
+ - Builder: 'docker build -t FOO' applies the tag FOO to the newly built container. |
|
| 11 |
+ - Runtime: interactive TTYs correctly handle window resize |
|
| 12 |
+ - Runtime: fix how configuration is merged between layers |
|
| 13 |
+ - Remote API: split stdout and stderr on 'docker run' |
|
| 14 |
+ - Remote API: optionally listen on a different IP and port (use at your own risk) |
|
| 15 |
+ - Documentation: improved install instructions. |
|
| 16 |
+ |
|
| 17 |
+ -- dotCloud <ops@dotcloud.com> Thu, 30 May 2013 00:00:00 -0700 |
|
| 18 |
+ |
|
| 19 |
+ |
|
| 1 | 20 |
lxc-docker (0.3.3-1) precise; urgency=low |
| 2 | 21 |
- Registry: Fix push regression |
| 3 | 22 |
- Various bugfixes |
| ... | ... |
@@ -15,7 +15,7 @@ import ( |
| 15 | 15 |
"strings" |
| 16 | 16 |
) |
| 17 | 17 |
|
| 18 |
-var ErrAlreadyExists error = errors.New("Image already exists")
|
|
| 18 |
+var ErrAlreadyExists = errors.New("Image already exists")
|
|
| 19 | 19 |
|
| 20 | 20 |
func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) {
|
| 21 | 21 |
for _, cookie := range c.Jar.Cookies(req.URL) {
|
| ... | ... |
@@ -64,7 +64,11 @@ func (r *Registry) LookupRemoteImage(imgId, registry string, authConfig *auth.Au |
| 64 | 64 |
} |
| 65 | 65 |
req.SetBasicAuth(authConfig.Username, authConfig.Password) |
| 66 | 66 |
res, err := rt.RoundTrip(req) |
| 67 |
- return err == nil && res.StatusCode == 307 |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ return false |
|
| 69 |
+ } |
|
| 70 |
+ res.Body.Close() |
|
| 71 |
+ return res.StatusCode == 307 |
|
| 68 | 72 |
} |
| 69 | 73 |
|
| 70 | 74 |
func (r *Registry) getImagesInRepository(repository string, authConfig *auth.AuthConfig) ([]map[string]string, error) {
|
| ... | ... |
@@ -103,8 +107,8 @@ func (r *Registry) getImagesInRepository(repository string, authConfig *auth.Aut |
| 103 | 103 |
|
| 104 | 104 |
// Retrieve an image from the Registry. |
| 105 | 105 |
// Returns the Image object as well as the layer as an Archive (io.Reader) |
| 106 |
-func (r *Registry) GetRemoteImageJson(imgId, registry string, token []string) ([]byte, error) {
|
|
| 107 |
- // Get the Json |
|
| 106 |
+func (r *Registry) GetRemoteImageJSON(imgId, registry string, token []string) ([]byte, error) {
|
|
| 107 |
+ // Get the JSON |
|
| 108 | 108 |
req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil)
|
| 109 | 109 |
if err != nil {
|
| 110 | 110 |
return nil, fmt.Errorf("Failed to download json: %s", err)
|
| ... | ... |
@@ -152,21 +156,24 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [ |
| 152 | 152 |
} |
| 153 | 153 |
req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
|
| 154 | 154 |
res, err := r.client.Do(req) |
| 155 |
- defer res.Body.Close() |
|
| 156 | 155 |
utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint)
|
| 157 |
- if err != nil || (res.StatusCode != 200 && res.StatusCode != 404) {
|
|
| 156 |
+ if err != nil {
|
|
| 157 |
+ return nil, err |
|
| 158 |
+ } |
|
| 159 |
+ defer res.Body.Close() |
|
| 160 |
+ |
|
| 161 |
+ if res.StatusCode != 200 && res.StatusCode != 404 {
|
|
| 158 | 162 |
continue |
| 159 | 163 |
} else if res.StatusCode == 404 {
|
| 160 | 164 |
return nil, fmt.Errorf("Repository not found")
|
| 161 | 165 |
} |
| 162 | 166 |
|
| 163 | 167 |
result := make(map[string]string) |
| 164 |
- |
|
| 165 |
- rawJson, err := ioutil.ReadAll(res.Body) |
|
| 168 |
+ rawJSON, err := ioutil.ReadAll(res.Body) |
|
| 166 | 169 |
if err != nil {
|
| 167 | 170 |
return nil, err |
| 168 | 171 |
} |
| 169 |
- if err := json.Unmarshal(rawJson, &result); err != nil {
|
|
| 172 |
+ if err := json.Unmarshal(rawJSON, &result); err != nil {
|
|
| 170 | 173 |
return nil, err |
| 171 | 174 |
} |
| 172 | 175 |
return result, nil |
| ... | ... |
@@ -212,19 +219,19 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
|
| 212 | 212 |
return nil, fmt.Errorf("Index response didn't contain any endpoints")
|
| 213 | 213 |
} |
| 214 | 214 |
|
| 215 |
- checksumsJson, err := ioutil.ReadAll(res.Body) |
|
| 215 |
+ checksumsJSON, err := ioutil.ReadAll(res.Body) |
|
| 216 | 216 |
if err != nil {
|
| 217 | 217 |
return nil, err |
| 218 | 218 |
} |
| 219 | 219 |
remoteChecksums := []*ImgData{}
|
| 220 |
- if err := json.Unmarshal(checksumsJson, &remoteChecksums); err != nil {
|
|
| 220 |
+ if err := json.Unmarshal(checksumsJSON, &remoteChecksums); err != nil {
|
|
| 221 | 221 |
return nil, err |
| 222 | 222 |
} |
| 223 | 223 |
|
| 224 | 224 |
// Forge a better object from the retrieved data |
| 225 | 225 |
imgsData := make(map[string]*ImgData) |
| 226 | 226 |
for _, elem := range remoteChecksums {
|
| 227 |
- imgsData[elem.Id] = elem |
|
| 227 |
+ imgsData[elem.ID] = elem |
|
| 228 | 228 |
} |
| 229 | 229 |
|
| 230 | 230 |
return &RepositoryData{
|
| ... | ... |
@@ -235,10 +242,10 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
|
| 235 | 235 |
} |
| 236 | 236 |
|
| 237 | 237 |
// Push a local image to the registry |
| 238 |
-func (r *Registry) PushImageJsonRegistry(imgData *ImgData, jsonRaw []byte, registry string, token []string) error {
|
|
| 238 |
+func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, registry string, token []string) error {
|
|
| 239 | 239 |
registry = "https://" + registry + "/v1" |
| 240 | 240 |
// FIXME: try json with UTF8 |
| 241 |
- req, err := http.NewRequest("PUT", registry+"/images/"+imgData.Id+"/json", strings.NewReader(string(jsonRaw)))
|
|
| 241 |
+ req, err := http.NewRequest("PUT", registry+"/images/"+imgData.ID+"/json", strings.NewReader(string(jsonRaw)))
|
|
| 242 | 242 |
if err != nil {
|
| 243 | 243 |
return err |
| 244 | 244 |
} |
| ... | ... |
@@ -246,7 +253,7 @@ func (r *Registry) PushImageJsonRegistry(imgData *ImgData, jsonRaw []byte, regis |
| 246 | 246 |
req.Header.Set("Authorization", "Token "+strings.Join(token, ","))
|
| 247 | 247 |
req.Header.Set("X-Docker-Checksum", imgData.Checksum)
|
| 248 | 248 |
|
| 249 |
- utils.Debugf("Setting checksum for %s: %s", imgData.Id, imgData.Checksum)
|
|
| 249 |
+ utils.Debugf("Setting checksum for %s: %s", imgData.ID, imgData.Checksum)
|
|
| 250 | 250 |
res, err := doWithCookies(r.client, req) |
| 251 | 251 |
if err != nil {
|
| 252 | 252 |
return fmt.Errorf("Failed to upload metadata: %s", err)
|
| ... | ... |
@@ -321,8 +328,8 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token |
| 321 | 321 |
return nil |
| 322 | 322 |
} |
| 323 | 323 |
|
| 324 |
-func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) {
|
|
| 325 |
- imgListJson, err := json.Marshal(imgList) |
|
| 324 |
+func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) {
|
|
| 325 |
+ imgListJSON, err := json.Marshal(imgList) |
|
| 326 | 326 |
if err != nil {
|
| 327 | 327 |
return nil, err |
| 328 | 328 |
} |
| ... | ... |
@@ -331,14 +338,14 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat |
| 331 | 331 |
suffix = "images" |
| 332 | 332 |
} |
| 333 | 333 |
|
| 334 |
- utils.Debugf("Image list pushed to index:\n%s\n", imgListJson)
|
|
| 334 |
+ utils.Debugf("Image list pushed to index:\n%s\n", imgListJSON)
|
|
| 335 | 335 |
|
| 336 |
- req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJson))
|
|
| 336 |
+ req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJSON))
|
|
| 337 | 337 |
if err != nil {
|
| 338 | 338 |
return nil, err |
| 339 | 339 |
} |
| 340 | 340 |
req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) |
| 341 |
- req.ContentLength = int64(len(imgListJson)) |
|
| 341 |
+ req.ContentLength = int64(len(imgListJSON)) |
|
| 342 | 342 |
req.Header.Set("X-Docker-Token", "true")
|
| 343 | 343 |
|
| 344 | 344 |
res, err := r.client.Do(req) |
| ... | ... |
@@ -350,12 +357,12 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat |
| 350 | 350 |
// Redirect if necessary |
| 351 | 351 |
for res.StatusCode >= 300 && res.StatusCode < 400 {
|
| 352 | 352 |
utils.Debugf("Redirected to %s\n", res.Header.Get("Location"))
|
| 353 |
- req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJson))
|
|
| 353 |
+ req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJSON))
|
|
| 354 | 354 |
if err != nil {
|
| 355 | 355 |
return nil, err |
| 356 | 356 |
} |
| 357 | 357 |
req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) |
| 358 |
- req.ContentLength = int64(len(imgListJson)) |
|
| 358 |
+ req.ContentLength = int64(len(imgListJSON)) |
|
| 359 | 359 |
req.Header.Set("X-Docker-Token", "true")
|
| 360 | 360 |
|
| 361 | 361 |
res, err = r.client.Do(req) |
| ... | ... |
@@ -389,11 +396,11 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat |
| 389 | 389 |
} |
| 390 | 390 |
if validate {
|
| 391 | 391 |
if res.StatusCode != 204 {
|
| 392 |
- if errBody, err := ioutil.ReadAll(res.Body); err != nil {
|
|
| 392 |
+ errBody, err := ioutil.ReadAll(res.Body) |
|
| 393 |
+ if err != nil {
|
|
| 393 | 394 |
return nil, err |
| 394 |
- } else {
|
|
| 395 |
- return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody)
|
|
| 396 | 395 |
} |
| 396 |
+ return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody)
|
|
| 397 | 397 |
} |
| 398 | 398 |
} |
| 399 | 399 |
|
| ... | ... |
@@ -456,7 +463,7 @@ type RepositoryData struct {
|
| 456 | 456 |
} |
| 457 | 457 |
|
| 458 | 458 |
type ImgData struct {
|
| 459 |
- Id string `json:"id"` |
|
| 459 |
+ ID string `json:"id"` |
|
| 460 | 460 |
Checksum string `json:"checksum,omitempty"` |
| 461 | 461 |
Tag string `json:",omitempty"` |
| 462 | 462 |
} |
| ... | ... |
@@ -470,9 +477,16 @@ func NewRegistry(root string) *Registry {
|
| 470 | 470 |
// If the auth file does not exist, keep going |
| 471 | 471 |
authConfig, _ := auth.LoadConfig(root) |
| 472 | 472 |
|
| 473 |
+ httpTransport := &http.Transport{
|
|
| 474 |
+ DisableKeepAlives: true, |
|
| 475 |
+ Proxy: http.ProxyFromEnvironment, |
|
| 476 |
+ } |
|
| 477 |
+ |
|
| 473 | 478 |
r := &Registry{
|
| 474 | 479 |
authConfig: authConfig, |
| 475 |
- client: &http.Client{},
|
|
| 480 |
+ client: &http.Client{
|
|
| 481 |
+ Transport: httpTransport, |
|
| 482 |
+ }, |
|
| 476 | 483 |
} |
| 477 | 484 |
r.client.Jar = cookiejar.NewCookieJar() |
| 478 | 485 |
return r |
| ... | ... |
@@ -51,7 +51,7 @@ func (runtime *Runtime) List() []*Container {
|
| 51 | 51 |
func (runtime *Runtime) getContainerElement(id string) *list.Element {
|
| 52 | 52 |
for e := runtime.containers.Front(); e != nil; e = e.Next() {
|
| 53 | 53 |
container := e.Value.(*Container) |
| 54 |
- if container.Id == id {
|
|
| 54 |
+ if container.ID == id {
|
|
| 55 | 55 |
return e |
| 56 | 56 |
} |
| 57 | 57 |
} |
| ... | ... |
@@ -83,8 +83,8 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
|
| 83 | 83 |
if err := container.FromDisk(); err != nil {
|
| 84 | 84 |
return nil, err |
| 85 | 85 |
} |
| 86 |
- if container.Id != id {
|
|
| 87 |
- return container, fmt.Errorf("Container %s is stored at %s", container.Id, id)
|
|
| 86 |
+ if container.ID != id {
|
|
| 87 |
+ return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
|
|
| 88 | 88 |
} |
| 89 | 89 |
if container.State.Running {
|
| 90 | 90 |
container.State.Ghost = true |
| ... | ... |
@@ -95,12 +95,12 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
|
| 95 | 95 |
return container, nil |
| 96 | 96 |
} |
| 97 | 97 |
|
| 98 |
-// Register makes a container object usable by the runtime as <container.Id> |
|
| 98 |
+// Register makes a container object usable by the runtime as <container.ID> |
|
| 99 | 99 |
func (runtime *Runtime) Register(container *Container) error {
|
| 100 |
- if container.runtime != nil || runtime.Exists(container.Id) {
|
|
| 100 |
+ if container.runtime != nil || runtime.Exists(container.ID) {
|
|
| 101 | 101 |
return fmt.Errorf("Container is already loaded")
|
| 102 | 102 |
} |
| 103 |
- if err := validateId(container.Id); err != nil {
|
|
| 103 |
+ if err := validateID(container.ID); err != nil {
|
|
| 104 | 104 |
return err |
| 105 | 105 |
} |
| 106 | 106 |
|
| ... | ... |
@@ -123,7 +123,7 @@ func (runtime *Runtime) Register(container *Container) error {
|
| 123 | 123 |
} |
| 124 | 124 |
// done |
| 125 | 125 |
runtime.containers.PushBack(container) |
| 126 |
- runtime.idIndex.Add(container.Id) |
|
| 126 |
+ runtime.idIndex.Add(container.ID) |
|
| 127 | 127 |
|
| 128 | 128 |
// When we actually restart, Start() do the monitoring. |
| 129 | 129 |
// However, when we simply 'reattach', we have to restart a monitor |
| ... | ... |
@@ -133,25 +133,25 @@ func (runtime *Runtime) Register(container *Container) error {
|
| 133 | 133 |
// if so, then we need to restart monitor and init a new lock |
| 134 | 134 |
// If the container is supposed to be running, make sure of it |
| 135 | 135 |
if container.State.Running {
|
| 136 |
- if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
|
|
| 136 |
+ output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
|
|
| 137 |
+ if err != nil {
|
|
| 137 | 138 |
return err |
| 138 |
- } else {
|
|
| 139 |
- if !strings.Contains(string(output), "RUNNING") {
|
|
| 140 |
- utils.Debugf("Container %s was supposed to be running be is not.", container.Id)
|
|
| 141 |
- if runtime.autoRestart {
|
|
| 142 |
- utils.Debugf("Restarting")
|
|
| 143 |
- container.State.Ghost = false |
|
| 144 |
- container.State.setStopped(0) |
|
| 145 |
- if err := container.Start(); err != nil {
|
|
| 146 |
- return err |
|
| 147 |
- } |
|
| 148 |
- nomonitor = true |
|
| 149 |
- } else {
|
|
| 150 |
- utils.Debugf("Marking as stopped")
|
|
| 151 |
- container.State.setStopped(-127) |
|
| 152 |
- if err := container.ToDisk(); err != nil {
|
|
| 153 |
- return err |
|
| 154 |
- } |
|
| 139 |
+ } |
|
| 140 |
+ if !strings.Contains(string(output), "RUNNING") {
|
|
| 141 |
+ utils.Debugf("Container %s was supposed to be running be is not.", container.ID)
|
|
| 142 |
+ if runtime.autoRestart {
|
|
| 143 |
+ utils.Debugf("Restarting")
|
|
| 144 |
+ container.State.Ghost = false |
|
| 145 |
+ container.State.setStopped(0) |
|
| 146 |
+ if err := container.Start(); err != nil {
|
|
| 147 |
+ return err |
|
| 148 |
+ } |
|
| 149 |
+ nomonitor = true |
|
| 150 |
+ } else {
|
|
| 151 |
+ utils.Debugf("Marking as stopped")
|
|
| 152 |
+ container.State.setStopped(-127) |
|
| 153 |
+ if err := container.ToDisk(); err != nil {
|
|
| 154 |
+ return err |
|
| 155 | 155 |
} |
| 156 | 156 |
} |
| 157 | 157 |
} |
| ... | ... |
@@ -182,9 +182,9 @@ func (runtime *Runtime) Destroy(container *Container) error {
|
| 182 | 182 |
return fmt.Errorf("The given container is <nil>")
|
| 183 | 183 |
} |
| 184 | 184 |
|
| 185 |
- element := runtime.getContainerElement(container.Id) |
|
| 185 |
+ element := runtime.getContainerElement(container.ID) |
|
| 186 | 186 |
if element == nil {
|
| 187 |
- return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.Id)
|
|
| 187 |
+ return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
|
|
| 188 | 188 |
} |
| 189 | 189 |
|
| 190 | 190 |
if err := container.Stop(3); err != nil {
|
| ... | ... |
@@ -194,14 +194,14 @@ func (runtime *Runtime) Destroy(container *Container) error {
|
| 194 | 194 |
return err |
| 195 | 195 |
} else if mounted {
|
| 196 | 196 |
if err := container.Unmount(); err != nil {
|
| 197 |
- return fmt.Errorf("Unable to unmount container %v: %v", container.Id, err)
|
|
| 197 |
+ return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
|
|
| 198 | 198 |
} |
| 199 | 199 |
} |
| 200 | 200 |
// Deregister the container before removing its directory, to avoid race conditions |
| 201 |
- runtime.idIndex.Delete(container.Id) |
|
| 201 |
+ runtime.idIndex.Delete(container.ID) |
|
| 202 | 202 |
runtime.containers.Remove(element) |
| 203 | 203 |
if err := os.RemoveAll(container.root); err != nil {
|
| 204 |
- return fmt.Errorf("Unable to remove filesystem for %v: %v", container.Id, err)
|
|
| 204 |
+ return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
|
|
| 205 | 205 |
} |
| 206 | 206 |
return nil |
| 207 | 207 |
} |
| ... | ... |
@@ -218,7 +218,7 @@ func (runtime *Runtime) restore() error {
|
| 218 | 218 |
utils.Debugf("Failed to load container %v: %v", id, err)
|
| 219 | 219 |
continue |
| 220 | 220 |
} |
| 221 |
- utils.Debugf("Loaded container %v", container.Id)
|
|
| 221 |
+ utils.Debugf("Loaded container %v", container.ID)
|
|
| 222 | 222 |
} |
| 223 | 223 |
return nil |
| 224 | 224 |
} |
| ... | ... |
@@ -68,7 +68,7 @@ func init() {
|
| 68 | 68 |
runtime: runtime, |
| 69 | 69 |
} |
| 70 | 70 |
// Retrieve the Image |
| 71 |
- if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, false); err != nil {
|
|
| 71 |
+ if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false)); err != nil {
|
|
| 72 | 72 |
panic(err) |
| 73 | 73 |
} |
| 74 | 74 |
} |
| ... | ... |
@@ -120,7 +120,7 @@ func TestRuntimeCreate(t *testing.T) {
|
| 120 | 120 |
builder := NewBuilder(runtime) |
| 121 | 121 |
|
| 122 | 122 |
container, err := builder.Create(&Config{
|
| 123 |
- Image: GetTestImage(runtime).Id, |
|
| 123 |
+ Image: GetTestImage(runtime).ID, |
|
| 124 | 124 |
Cmd: []string{"ls", "-al"},
|
| 125 | 125 |
}, |
| 126 | 126 |
) |
| ... | ... |
@@ -140,29 +140,29 @@ func TestRuntimeCreate(t *testing.T) {
|
| 140 | 140 |
} |
| 141 | 141 |
|
| 142 | 142 |
// Make sure the container List() returns is the right one |
| 143 |
- if runtime.List()[0].Id != container.Id {
|
|
| 143 |
+ if runtime.List()[0].ID != container.ID {
|
|
| 144 | 144 |
t.Errorf("Unexpected container %v returned by List", runtime.List()[0])
|
| 145 | 145 |
} |
| 146 | 146 |
|
| 147 | 147 |
// Make sure we can get the container with Get() |
| 148 |
- if runtime.Get(container.Id) == nil {
|
|
| 148 |
+ if runtime.Get(container.ID) == nil {
|
|
| 149 | 149 |
t.Errorf("Unable to get newly created container")
|
| 150 | 150 |
} |
| 151 | 151 |
|
| 152 | 152 |
// Make sure it is the right container |
| 153 |
- if runtime.Get(container.Id) != container {
|
|
| 153 |
+ if runtime.Get(container.ID) != container {
|
|
| 154 | 154 |
t.Errorf("Get() returned the wrong container")
|
| 155 | 155 |
} |
| 156 | 156 |
|
| 157 | 157 |
// Make sure Exists returns it as existing |
| 158 |
- if !runtime.Exists(container.Id) {
|
|
| 158 |
+ if !runtime.Exists(container.ID) {
|
|
| 159 | 159 |
t.Errorf("Exists() returned false for a newly created container")
|
| 160 | 160 |
} |
| 161 | 161 |
|
| 162 | 162 |
// Make sure crete with bad parameters returns an error |
| 163 | 163 |
_, err = builder.Create( |
| 164 | 164 |
&Config{
|
| 165 |
- Image: GetTestImage(runtime).Id, |
|
| 165 |
+ Image: GetTestImage(runtime).ID, |
|
| 166 | 166 |
}, |
| 167 | 167 |
) |
| 168 | 168 |
if err == nil {
|
| ... | ... |
@@ -171,7 +171,7 @@ func TestRuntimeCreate(t *testing.T) {
|
| 171 | 171 |
|
| 172 | 172 |
_, err = builder.Create( |
| 173 | 173 |
&Config{
|
| 174 |
- Image: GetTestImage(runtime).Id, |
|
| 174 |
+ Image: GetTestImage(runtime).ID, |
|
| 175 | 175 |
Cmd: []string{},
|
| 176 | 176 |
}, |
| 177 | 177 |
) |
| ... | ... |
@@ -187,7 +187,7 @@ func TestDestroy(t *testing.T) {
|
| 187 | 187 |
} |
| 188 | 188 |
defer nuke(runtime) |
| 189 | 189 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 190 |
- Image: GetTestImage(runtime).Id, |
|
| 190 |
+ Image: GetTestImage(runtime).ID, |
|
| 191 | 191 |
Cmd: []string{"ls", "-al"},
|
| 192 | 192 |
}, |
| 193 | 193 |
) |
| ... | ... |
@@ -210,7 +210,7 @@ func TestDestroy(t *testing.T) {
|
| 210 | 210 |
} |
| 211 | 211 |
|
| 212 | 212 |
// Make sure runtime.Get() refuses to return the unexisting container |
| 213 |
- if runtime.Get(container.Id) != nil {
|
|
| 213 |
+ if runtime.Get(container.ID) != nil {
|
|
| 214 | 214 |
t.Errorf("Unable to get newly created container")
|
| 215 | 215 |
} |
| 216 | 216 |
|
| ... | ... |
@@ -237,7 +237,7 @@ func TestGet(t *testing.T) {
|
| 237 | 237 |
builder := NewBuilder(runtime) |
| 238 | 238 |
|
| 239 | 239 |
container1, err := builder.Create(&Config{
|
| 240 |
- Image: GetTestImage(runtime).Id, |
|
| 240 |
+ Image: GetTestImage(runtime).ID, |
|
| 241 | 241 |
Cmd: []string{"ls", "-al"},
|
| 242 | 242 |
}, |
| 243 | 243 |
) |
| ... | ... |
@@ -247,7 +247,7 @@ func TestGet(t *testing.T) {
|
| 247 | 247 |
defer runtime.Destroy(container1) |
| 248 | 248 |
|
| 249 | 249 |
container2, err := builder.Create(&Config{
|
| 250 |
- Image: GetTestImage(runtime).Id, |
|
| 250 |
+ Image: GetTestImage(runtime).ID, |
|
| 251 | 251 |
Cmd: []string{"ls", "-al"},
|
| 252 | 252 |
}, |
| 253 | 253 |
) |
| ... | ... |
@@ -257,7 +257,7 @@ func TestGet(t *testing.T) {
|
| 257 | 257 |
defer runtime.Destroy(container2) |
| 258 | 258 |
|
| 259 | 259 |
container3, err := builder.Create(&Config{
|
| 260 |
- Image: GetTestImage(runtime).Id, |
|
| 260 |
+ Image: GetTestImage(runtime).ID, |
|
| 261 | 261 |
Cmd: []string{"ls", "-al"},
|
| 262 | 262 |
}, |
| 263 | 263 |
) |
| ... | ... |
@@ -266,16 +266,16 @@ func TestGet(t *testing.T) {
|
| 266 | 266 |
} |
| 267 | 267 |
defer runtime.Destroy(container3) |
| 268 | 268 |
|
| 269 |
- if runtime.Get(container1.Id) != container1 {
|
|
| 270 |
- t.Errorf("Get(test1) returned %v while expecting %v", runtime.Get(container1.Id), container1)
|
|
| 269 |
+ if runtime.Get(container1.ID) != container1 {
|
|
| 270 |
+ t.Errorf("Get(test1) returned %v while expecting %v", runtime.Get(container1.ID), container1)
|
|
| 271 | 271 |
} |
| 272 | 272 |
|
| 273 |
- if runtime.Get(container2.Id) != container2 {
|
|
| 274 |
- t.Errorf("Get(test2) returned %v while expecting %v", runtime.Get(container2.Id), container2)
|
|
| 273 |
+ if runtime.Get(container2.ID) != container2 {
|
|
| 274 |
+ t.Errorf("Get(test2) returned %v while expecting %v", runtime.Get(container2.ID), container2)
|
|
| 275 | 275 |
} |
| 276 | 276 |
|
| 277 |
- if runtime.Get(container3.Id) != container3 {
|
|
| 278 |
- t.Errorf("Get(test3) returned %v while expecting %v", runtime.Get(container3.Id), container3)
|
|
| 277 |
+ if runtime.Get(container3.ID) != container3 {
|
|
| 278 |
+ t.Errorf("Get(test3) returned %v while expecting %v", runtime.Get(container3.ID), container3)
|
|
| 279 | 279 |
} |
| 280 | 280 |
|
| 281 | 281 |
} |
| ... | ... |
@@ -283,7 +283,7 @@ func TestGet(t *testing.T) {
|
| 283 | 283 |
func findAvailalblePort(runtime *Runtime, port int) (*Container, error) {
|
| 284 | 284 |
strPort := strconv.Itoa(port) |
| 285 | 285 |
container, err := NewBuilder(runtime).Create(&Config{
|
| 286 |
- Image: GetTestImage(runtime).Id, |
|
| 286 |
+ Image: GetTestImage(runtime).ID, |
|
| 287 | 287 |
Cmd: []string{"sh", "-c", "echo well hello there | nc -l -p " + strPort},
|
| 288 | 288 |
PortSpecs: []string{strPort},
|
| 289 | 289 |
}, |
| ... | ... |
@@ -379,7 +379,7 @@ func TestRestore(t *testing.T) {
|
| 379 | 379 |
|
| 380 | 380 |
// Create a container with one instance of docker |
| 381 | 381 |
container1, err := builder.Create(&Config{
|
| 382 |
- Image: GetTestImage(runtime1).Id, |
|
| 382 |
+ Image: GetTestImage(runtime1).ID, |
|
| 383 | 383 |
Cmd: []string{"ls", "-al"},
|
| 384 | 384 |
}, |
| 385 | 385 |
) |
| ... | ... |
@@ -390,7 +390,7 @@ func TestRestore(t *testing.T) {
|
| 390 | 390 |
|
| 391 | 391 |
// Create a second container meant to be killed |
| 392 | 392 |
container2, err := builder.Create(&Config{
|
| 393 |
- Image: GetTestImage(runtime1).Id, |
|
| 393 |
+ Image: GetTestImage(runtime1).ID, |
|
| 394 | 394 |
Cmd: []string{"/bin/cat"},
|
| 395 | 395 |
OpenStdin: true, |
| 396 | 396 |
}, |
| ... | ... |
@@ -406,7 +406,7 @@ func TestRestore(t *testing.T) {
|
| 406 | 406 |
} |
| 407 | 407 |
|
| 408 | 408 |
if !container2.State.Running {
|
| 409 |
- t.Fatalf("Container %v should appear as running but isn't", container2.Id)
|
|
| 409 |
+ t.Fatalf("Container %v should appear as running but isn't", container2.ID)
|
|
| 410 | 410 |
} |
| 411 | 411 |
|
| 412 | 412 |
// Simulate a crash/manual quit of dockerd: process dies, states stays 'Running' |
| ... | ... |
@@ -426,7 +426,7 @@ func TestRestore(t *testing.T) {
|
| 426 | 426 |
} |
| 427 | 427 |
|
| 428 | 428 |
if !container2.State.Running {
|
| 429 |
- t.Fatalf("Container %v should appear as running but isn't", container2.Id)
|
|
| 429 |
+ t.Fatalf("Container %v should appear as running but isn't", container2.ID)
|
|
| 430 | 430 |
} |
| 431 | 431 |
|
| 432 | 432 |
// Here are are simulating a docker restart - that is, reloading all containers |
| ... | ... |
@@ -442,14 +442,14 @@ func TestRestore(t *testing.T) {
|
| 442 | 442 |
runningCount := 0 |
| 443 | 443 |
for _, c := range runtime2.List() {
|
| 444 | 444 |
if c.State.Running {
|
| 445 |
- t.Errorf("Running container found: %v (%v)", c.Id, c.Path)
|
|
| 445 |
+ t.Errorf("Running container found: %v (%v)", c.ID, c.Path)
|
|
| 446 | 446 |
runningCount++ |
| 447 | 447 |
} |
| 448 | 448 |
} |
| 449 | 449 |
if runningCount != 0 {
|
| 450 | 450 |
t.Fatalf("Expected 0 container alive, %d found", runningCount)
|
| 451 | 451 |
} |
| 452 |
- container3 := runtime2.Get(container1.Id) |
|
| 452 |
+ container3 := runtime2.Get(container1.ID) |
|
| 453 | 453 |
if container3 == nil {
|
| 454 | 454 |
t.Fatal("Unable to Get container")
|
| 455 | 455 |
} |
| ... | ... |
@@ -16,8 +16,12 @@ import ( |
| 16 | 16 |
"strings" |
| 17 | 17 |
) |
| 18 | 18 |
|
| 19 |
-func (srv *Server) DockerVersion() ApiVersion {
|
|
| 20 |
- return ApiVersion{VERSION, GIT_COMMIT, srv.runtime.capabilities.MemoryLimit, srv.runtime.capabilities.SwapLimit}
|
|
| 19 |
+func (srv *Server) DockerVersion() APIVersion {
|
|
| 20 |
+ return APIVersion{
|
|
| 21 |
+ Version: VERSION, |
|
| 22 |
+ GitCommit: GITCOMMIT, |
|
| 23 |
+ GoVersion: runtime.Version(), |
|
| 24 |
+ } |
|
| 21 | 25 |
} |
| 22 | 26 |
|
| 23 | 27 |
func (srv *Server) ContainerKill(name string) error {
|
| ... | ... |
@@ -48,16 +52,16 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
|
| 48 | 48 |
return fmt.Errorf("No such container: %s", name)
|
| 49 | 49 |
} |
| 50 | 50 |
|
| 51 |
-func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) {
|
|
| 51 |
+func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
|
|
| 52 | 52 |
|
| 53 | 53 |
results, err := registry.NewRegistry(srv.runtime.root).SearchRepositories(term) |
| 54 | 54 |
if err != nil {
|
| 55 | 55 |
return nil, err |
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 |
- var outs []ApiSearch |
|
| 58 |
+ var outs []APISearch |
|
| 59 | 59 |
for _, repo := range results.Results {
|
| 60 |
- var out ApiSearch |
|
| 60 |
+ var out APISearch |
|
| 61 | 61 |
out.Description = repo["description"] |
| 62 | 62 |
if len(out.Description) > 45 {
|
| 63 | 63 |
out.Description = utils.Trunc(out.Description, 42) + "..." |
| ... | ... |
@@ -68,7 +72,7 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) {
|
| 68 | 68 |
return outs, nil |
| 69 | 69 |
} |
| 70 | 70 |
|
| 71 |
-func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, error) {
|
|
| 71 |
+func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) (string, error) {
|
|
| 72 | 72 |
out = utils.NewWriteFlusher(out) |
| 73 | 73 |
img, err := srv.runtime.repositories.LookupImage(name) |
| 74 | 74 |
if err != nil {
|
| ... | ... |
@@ -81,7 +85,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e |
| 81 | 81 |
} |
| 82 | 82 |
defer file.Body.Close() |
| 83 | 83 |
|
| 84 |
- config, _, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, srv.runtime.capabilities)
|
|
| 84 |
+ config, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities)
|
|
| 85 | 85 |
if err != nil {
|
| 86 | 86 |
return "", err |
| 87 | 87 |
} |
| ... | ... |
@@ -92,7 +96,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e |
| 92 | 92 |
return "", err |
| 93 | 93 |
} |
| 94 | 94 |
|
| 95 |
- if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)\r", false), path); err != nil {
|
|
| 95 |
+ if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), path); err != nil {
|
|
| 96 | 96 |
return "", err |
| 97 | 97 |
} |
| 98 | 98 |
// FIXME: Handle custom repo, tag comment, author |
| ... | ... |
@@ -100,8 +104,8 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e |
| 100 | 100 |
if err != nil {
|
| 101 | 101 |
return "", err |
| 102 | 102 |
} |
| 103 |
- fmt.Fprintf(out, "%s\n", img.Id) |
|
| 104 |
- return img.ShortId(), nil |
|
| 103 |
+ out.Write(sf.FormatStatus(img.ID)) |
|
| 104 |
+ return img.ShortID(), nil |
|
| 105 | 105 |
} |
| 106 | 106 |
|
| 107 | 107 |
func (srv *Server) ImagesViz(out io.Writer) error {
|
| ... | ... |
@@ -121,9 +125,9 @@ func (srv *Server) ImagesViz(out io.Writer) error {
|
| 121 | 121 |
return fmt.Errorf("Error while getting parent image: %v", err)
|
| 122 | 122 |
} |
| 123 | 123 |
if parentImage != nil {
|
| 124 |
- out.Write([]byte(" \"" + parentImage.ShortId() + "\" -> \"" + image.ShortId() + "\"\n"))
|
|
| 124 |
+ out.Write([]byte(" \"" + parentImage.ShortID() + "\" -> \"" + image.ShortID() + "\"\n"))
|
|
| 125 | 125 |
} else {
|
| 126 |
- out.Write([]byte(" base -> \"" + image.ShortId() + "\" [style=invis]\n"))
|
|
| 126 |
+ out.Write([]byte(" base -> \"" + image.ShortID() + "\" [style=invis]\n"))
|
|
| 127 | 127 |
} |
| 128 | 128 |
} |
| 129 | 129 |
|
| ... | ... |
@@ -131,7 +135,7 @@ func (srv *Server) ImagesViz(out io.Writer) error {
|
| 131 | 131 |
|
| 132 | 132 |
for name, repository := range srv.runtime.repositories.Repositories {
|
| 133 | 133 |
for tag, id := range repository {
|
| 134 |
- reporefs[utils.TruncateId(id)] = append(reporefs[utils.TruncateId(id)], fmt.Sprintf("%s:%s", name, tag))
|
|
| 134 |
+ reporefs[utils.TruncateID(id)] = append(reporefs[utils.TruncateID(id)], fmt.Sprintf("%s:%s", name, tag))
|
|
| 135 | 135 |
} |
| 136 | 136 |
} |
| 137 | 137 |
|
| ... | ... |
@@ -142,7 +146,7 @@ func (srv *Server) ImagesViz(out io.Writer) error {
|
| 142 | 142 |
return nil |
| 143 | 143 |
} |
| 144 | 144 |
|
| 145 |
-func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
|
|
| 145 |
+func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
|
|
| 146 | 146 |
var ( |
| 147 | 147 |
allImages map[string]*Image |
| 148 | 148 |
err error |
| ... | ... |
@@ -155,13 +159,13 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
|
| 155 | 155 |
if err != nil {
|
| 156 | 156 |
return nil, err |
| 157 | 157 |
} |
| 158 |
- outs := []ApiImages{} //produce [] when empty instead of 'null'
|
|
| 158 |
+ outs := []APIImages{} //produce [] when empty instead of 'null'
|
|
| 159 | 159 |
for name, repository := range srv.runtime.repositories.Repositories {
|
| 160 | 160 |
if filter != "" && name != filter {
|
| 161 | 161 |
continue |
| 162 | 162 |
} |
| 163 | 163 |
for tag, id := range repository {
|
| 164 |
- var out ApiImages |
|
| 164 |
+ var out APIImages |
|
| 165 | 165 |
image, err := srv.runtime.graph.Get(id) |
| 166 | 166 |
if err != nil {
|
| 167 | 167 |
log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
|
| ... | ... |
@@ -170,7 +174,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
|
| 170 | 170 |
delete(allImages, id) |
| 171 | 171 |
out.Repository = name |
| 172 | 172 |
out.Tag = tag |
| 173 |
- out.Id = image.Id |
|
| 173 |
+ out.ID = image.ID |
|
| 174 | 174 |
out.Created = image.Created.Unix() |
| 175 | 175 |
out.Size = image.Size |
| 176 | 176 |
out.ParentSize = image.getVirtualSize(0) |
| ... | ... |
@@ -180,8 +184,8 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
|
| 180 | 180 |
// Display images which aren't part of a |
| 181 | 181 |
if filter == "" {
|
| 182 | 182 |
for _, image := range allImages {
|
| 183 |
- var out ApiImages |
|
| 184 |
- out.Id = image.Id |
|
| 183 |
+ var out APIImages |
|
| 184 |
+ out.ID = image.ID |
|
| 185 | 185 |
out.Created = image.Created.Unix() |
| 186 | 186 |
out.Size = image.Size |
| 187 | 187 |
out.ParentSize = image.getVirtualSize(0) |
| ... | ... |
@@ -191,7 +195,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
|
| 191 | 191 |
return outs, nil |
| 192 | 192 |
} |
| 193 | 193 |
|
| 194 |
-func (srv *Server) DockerInfo() ApiInfo {
|
|
| 194 |
+func (srv *Server) DockerInfo() *APIInfo {
|
|
| 195 | 195 |
images, _ := srv.runtime.graph.All() |
| 196 | 196 |
var imgcount int |
| 197 | 197 |
if images == nil {
|
| ... | ... |
@@ -199,29 +203,27 @@ func (srv *Server) DockerInfo() ApiInfo {
|
| 199 | 199 |
} else {
|
| 200 | 200 |
imgcount = len(images) |
| 201 | 201 |
} |
| 202 |
- var out ApiInfo |
|
| 203 |
- out.Containers = len(srv.runtime.List()) |
|
| 204 |
- out.Version = VERSION |
|
| 205 |
- out.Images = imgcount |
|
| 206 |
- out.GoVersion = runtime.Version() |
|
| 207 |
- if os.Getenv("DEBUG") != "" {
|
|
| 208 |
- out.Debug = true |
|
| 209 |
- out.NFd = utils.GetTotalUsedFds() |
|
| 210 |
- out.NGoroutines = runtime.NumGoroutine() |
|
| 202 |
+ return &APIInfo{
|
|
| 203 |
+ Containers: len(srv.runtime.List()), |
|
| 204 |
+ Images: imgcount, |
|
| 205 |
+ MemoryLimit: srv.runtime.capabilities.MemoryLimit, |
|
| 206 |
+ SwapLimit: srv.runtime.capabilities.SwapLimit, |
|
| 207 |
+ Debug: os.Getenv("DEBUG") != "",
|
|
| 208 |
+ NFd: utils.GetTotalUsedFds(), |
|
| 209 |
+ NGoroutines: runtime.NumGoroutine(), |
|
| 211 | 210 |
} |
| 212 |
- return out |
|
| 213 | 211 |
} |
| 214 | 212 |
|
| 215 |
-func (srv *Server) ImageHistory(name string) ([]ApiHistory, error) {
|
|
| 213 |
+func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
|
|
| 216 | 214 |
image, err := srv.runtime.repositories.LookupImage(name) |
| 217 | 215 |
if err != nil {
|
| 218 | 216 |
return nil, err |
| 219 | 217 |
} |
| 220 | 218 |
|
| 221 |
- var outs []ApiHistory = []ApiHistory{} //produce [] when empty instead of 'null'
|
|
| 219 |
+ outs := []APIHistory{} //produce [] when empty instead of 'null'
|
|
| 222 | 220 |
err = image.WalkHistory(func(img *Image) error {
|
| 223 |
- var out ApiHistory |
|
| 224 |
- out.Id = srv.runtime.repositories.ImageName(img.ShortId()) |
|
| 221 |
+ var out APIHistory |
|
| 222 |
+ out.ID = srv.runtime.repositories.ImageName(img.ShortID()) |
|
| 225 | 223 |
out.Created = img.Created.Unix() |
| 226 | 224 |
out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ") |
| 227 | 225 |
outs = append(outs, out) |
| ... | ... |
@@ -238,17 +240,17 @@ func (srv *Server) ContainerChanges(name string) ([]Change, error) {
|
| 238 | 238 |
return nil, fmt.Errorf("No such container: %s", name)
|
| 239 | 239 |
} |
| 240 | 240 |
|
| 241 |
-func (srv *Server) Containers(all bool, n int, since, before string) []ApiContainers {
|
|
| 241 |
+func (srv *Server) Containers(all bool, n int, since, before string) []APIContainers {
|
|
| 242 | 242 |
var foundBefore bool |
| 243 | 243 |
var displayed int |
| 244 |
- retContainers := []ApiContainers{}
|
|
| 244 |
+ retContainers := []APIContainers{}
|
|
| 245 | 245 |
|
| 246 | 246 |
for _, container := range srv.runtime.List() {
|
| 247 | 247 |
if !container.State.Running && !all && n == -1 && since == "" && before == "" {
|
| 248 | 248 |
continue |
| 249 | 249 |
} |
| 250 | 250 |
if before != "" {
|
| 251 |
- if container.ShortId() == before {
|
|
| 251 |
+ if container.ShortID() == before {
|
|
| 252 | 252 |
foundBefore = true |
| 253 | 253 |
continue |
| 254 | 254 |
} |
| ... | ... |
@@ -259,13 +261,13 @@ func (srv *Server) Containers(all bool, n int, since, before string) []ApiContai |
| 259 | 259 |
if displayed == n {
|
| 260 | 260 |
break |
| 261 | 261 |
} |
| 262 |
- if container.ShortId() == since {
|
|
| 262 |
+ if container.ShortID() == since {
|
|
| 263 | 263 |
break |
| 264 | 264 |
} |
| 265 | 265 |
displayed++ |
| 266 | 266 |
|
| 267 |
- c := ApiContainers{
|
|
| 268 |
- Id: container.Id, |
|
| 267 |
+ c := APIContainers{
|
|
| 268 |
+ ID: container.ID, |
|
| 269 | 269 |
} |
| 270 | 270 |
c.Image = srv.runtime.repositories.ImageName(container.Image) |
| 271 | 271 |
c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
|
| ... | ... |
@@ -288,7 +290,7 @@ func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, conf |
| 288 | 288 |
if err != nil {
|
| 289 | 289 |
return "", err |
| 290 | 290 |
} |
| 291 |
- return img.ShortId(), err |
|
| 291 |
+ return img.ShortID(), err |
|
| 292 | 292 |
} |
| 293 | 293 |
|
| 294 | 294 |
func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
|
| ... | ... |
@@ -298,7 +300,7 @@ func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
|
| 298 | 298 |
return nil |
| 299 | 299 |
} |
| 300 | 300 |
|
| 301 |
-func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoint string, token []string, json bool) error {
|
|
| 301 |
+func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoint string, token []string, sf *utils.StreamFormatter) error {
|
|
| 302 | 302 |
history, err := r.GetRemoteHistory(imgId, endpoint, token) |
| 303 | 303 |
if err != nil {
|
| 304 | 304 |
return err |
| ... | ... |
@@ -308,24 +310,25 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin |
| 308 | 308 |
// FIXME: Launch the getRemoteImage() in goroutines |
| 309 | 309 |
for _, id := range history {
|
| 310 | 310 |
if !srv.runtime.graph.Exists(id) {
|
| 311 |
- fmt.Fprintf(out, utils.FormatStatus("Pulling %s metadata", json), id)
|
|
| 312 |
- imgJson, err := r.GetRemoteImageJson(id, endpoint, token) |
|
| 311 |
+ out.Write(sf.FormatStatus("Pulling %s metadata", id))
|
|
| 312 |
+ imgJSON, err := r.GetRemoteImageJSON(id, endpoint, token) |
|
| 313 | 313 |
if err != nil {
|
| 314 | 314 |
// FIXME: Keep goging in case of error? |
| 315 | 315 |
return err |
| 316 | 316 |
} |
| 317 |
- img, err := NewImgJson(imgJson) |
|
| 317 |
+ img, err := NewImgJSON(imgJSON) |
|
| 318 | 318 |
if err != nil {
|
| 319 | 319 |
return fmt.Errorf("Failed to parse json: %s", err)
|
| 320 | 320 |
} |
| 321 | 321 |
|
| 322 | 322 |
// Get the layer |
| 323 |
- fmt.Fprintf(out, utils.FormatStatus("Pulling %s fs layer", json), id)
|
|
| 324 |
- layer, contentLength, err := r.GetRemoteImageLayer(img.Id, endpoint, token) |
|
| 323 |
+ out.Write(sf.FormatStatus("Pulling %s fs layer", id))
|
|
| 324 |
+ layer, contentLength, err := r.GetRemoteImageLayer(img.ID, endpoint, token) |
|
| 325 | 325 |
if err != nil {
|
| 326 | 326 |
return err |
| 327 | 327 |
} |
| 328 |
- if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, utils.FormatProgress("%v/%v (%v)", json), json), false, img); err != nil {
|
|
| 328 |
+ defer layer.Close() |
|
| 329 |
+ if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), false, img); err != nil {
|
|
| 329 | 330 |
return err |
| 330 | 331 |
} |
| 331 | 332 |
} |
| ... | ... |
@@ -333,8 +336,8 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin |
| 333 | 333 |
return nil |
| 334 | 334 |
} |
| 335 | 335 |
|
| 336 |
-func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, json bool) error {
|
|
| 337 |
- fmt.Fprintf(out, utils.FormatStatus("Pulling repository %s from %s", json), remote, auth.IndexServerAddress())
|
|
| 336 |
+func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, sf *utils.StreamFormatter) error {
|
|
| 337 |
+ out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress()))
|
|
| 338 | 338 |
repoData, err := r.GetRepositoryData(remote) |
| 339 | 339 |
if err != nil {
|
| 340 | 340 |
return err |
| ... | ... |
@@ -359,23 +362,23 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a |
| 359 | 359 |
} |
| 360 | 360 |
} else {
|
| 361 | 361 |
// Otherwise, check that the tag exists and use only that one |
| 362 |
- if id, exists := tagsList[askedTag]; !exists {
|
|
| 362 |
+ id, exists := tagsList[askedTag] |
|
| 363 |
+ if !exists {
|
|
| 363 | 364 |
return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, remote)
|
| 364 |
- } else {
|
|
| 365 |
- repoData.ImgList[id].Tag = askedTag |
|
| 366 | 365 |
} |
| 366 |
+ repoData.ImgList[id].Tag = askedTag |
|
| 367 | 367 |
} |
| 368 | 368 |
|
| 369 | 369 |
for _, img := range repoData.ImgList {
|
| 370 | 370 |
if askedTag != "" && img.Tag != askedTag {
|
| 371 |
- utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.Id)
|
|
| 371 |
+ utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.ID)
|
|
| 372 | 372 |
continue |
| 373 | 373 |
} |
| 374 |
- fmt.Fprintf(out, utils.FormatStatus("Pulling image %s (%s) from %s", json), img.Id, img.Tag, remote)
|
|
| 374 |
+ out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", img.ID, img.Tag, remote))
|
|
| 375 | 375 |
success := false |
| 376 | 376 |
for _, ep := range repoData.Endpoints {
|
| 377 |
- if err := srv.pullImage(r, out, img.Id, "https://"+ep+"/v1", repoData.Tokens, json); err != nil {
|
|
| 378 |
- fmt.Fprintf(out, utils.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint\n", json), askedTag, err)
|
|
| 377 |
+ if err := srv.pullImage(r, out, img.ID, "https://"+ep+"/v1", repoData.Tokens, sf); err != nil {
|
|
| 378 |
+ out.Write(sf.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint", askedTag, err))
|
|
| 379 | 379 |
continue |
| 380 | 380 |
} |
| 381 | 381 |
success = true |
| ... | ... |
@@ -400,17 +403,17 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a |
| 400 | 400 |
return nil |
| 401 | 401 |
} |
| 402 | 402 |
|
| 403 |
-func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, json bool) error {
|
|
| 403 |
+func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
|
|
| 404 | 404 |
r := registry.NewRegistry(srv.runtime.root) |
| 405 | 405 |
out = utils.NewWriteFlusher(out) |
| 406 | 406 |
if endpoint != "" {
|
| 407 |
- if err := srv.pullImage(r, out, name, endpoint, nil, json); err != nil {
|
|
| 407 |
+ if err := srv.pullImage(r, out, name, endpoint, nil, sf); err != nil {
|
|
| 408 | 408 |
return err |
| 409 | 409 |
} |
| 410 | 410 |
return nil |
| 411 | 411 |
} |
| 412 | 412 |
|
| 413 |
- if err := srv.pullRepository(r, out, name, tag, json); err != nil {
|
|
| 413 |
+ if err := srv.pullRepository(r, out, name, tag, sf); err != nil {
|
|
| 414 | 414 |
return err |
| 415 | 415 |
} |
| 416 | 416 |
|
| ... | ... |
@@ -464,16 +467,16 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat |
| 464 | 464 |
return nil, err |
| 465 | 465 |
} |
| 466 | 466 |
img.WalkHistory(func(img *Image) error {
|
| 467 |
- if _, exists := imageSet[img.Id]; exists {
|
|
| 467 |
+ if _, exists := imageSet[img.ID]; exists {
|
|
| 468 | 468 |
return nil |
| 469 | 469 |
} |
| 470 |
- imageSet[img.Id] = struct{}{}
|
|
| 471 |
- checksum, err := srv.getChecksum(img.Id) |
|
| 470 |
+ imageSet[img.ID] = struct{}{}
|
|
| 471 |
+ checksum, err := srv.getChecksum(img.ID) |
|
| 472 | 472 |
if err != nil {
|
| 473 | 473 |
return err |
| 474 | 474 |
} |
| 475 | 475 |
imgList = append([]*registry.ImgData{{
|
| 476 |
- Id: img.Id, |
|
| 476 |
+ ID: img.ID, |
|
| 477 | 477 |
Checksum: checksum, |
| 478 | 478 |
Tag: tag, |
| 479 | 479 |
}}, imgList...) |
| ... | ... |
@@ -483,52 +486,52 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat |
| 483 | 483 |
return imgList, nil |
| 484 | 484 |
} |
| 485 | 485 |
|
| 486 |
-func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name string, localRepo map[string]string) error {
|
|
| 486 |
+func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name string, localRepo map[string]string, sf *utils.StreamFormatter) error {
|
|
| 487 | 487 |
out = utils.NewWriteFlusher(out) |
| 488 |
- fmt.Fprintf(out, "Processing checksums\n") |
|
| 488 |
+ out.Write(sf.FormatStatus("Processing checksums"))
|
|
| 489 | 489 |
imgList, err := srv.getImageList(localRepo) |
| 490 | 490 |
if err != nil {
|
| 491 | 491 |
return err |
| 492 | 492 |
} |
| 493 |
- fmt.Fprintf(out, "Sending images list\n") |
|
| 493 |
+ out.Write(sf.FormatStatus("Sending image list"))
|
|
| 494 | 494 |
|
| 495 |
- repoData, err := r.PushImageJsonIndex(name, imgList, false) |
|
| 495 |
+ repoData, err := r.PushImageJSONIndex(name, imgList, false) |
|
| 496 | 496 |
if err != nil {
|
| 497 | 497 |
return err |
| 498 | 498 |
} |
| 499 | 499 |
|
| 500 | 500 |
for _, ep := range repoData.Endpoints {
|
| 501 |
- fmt.Fprintf(out, "Pushing repository %s to %s (%d tags)\r\n", name, ep, len(localRepo)) |
|
| 501 |
+ out.Write(sf.FormatStatus("Pushing repository %s to %s (%d tags)", name, ep, len(localRepo)))
|
|
| 502 | 502 |
// For each image within the repo, push them |
| 503 | 503 |
for _, elem := range imgList {
|
| 504 |
- if _, exists := repoData.ImgList[elem.Id]; exists {
|
|
| 505 |
- fmt.Fprintf(out, "Image %s already on registry, skipping\n", name) |
|
| 504 |
+ if _, exists := repoData.ImgList[elem.ID]; exists {
|
|
| 505 |
+ out.Write(sf.FormatStatus("Image %s already on registry, skipping", name))
|
|
| 506 | 506 |
continue |
| 507 | 507 |
} |
| 508 |
- if err := srv.pushImage(r, out, name, elem.Id, ep, repoData.Tokens); err != nil {
|
|
| 508 |
+ if err := srv.pushImage(r, out, name, elem.ID, ep, repoData.Tokens, sf); err != nil {
|
|
| 509 | 509 |
// FIXME: Continue on error? |
| 510 | 510 |
return err |
| 511 | 511 |
} |
| 512 |
- fmt.Fprintf(out, "Pushing tags for rev [%s] on {%s}\n", elem.Id, ep+"/users/"+name+"/"+elem.Tag)
|
|
| 513 |
- if err := r.PushRegistryTag(name, elem.Id, elem.Tag, ep, repoData.Tokens); err != nil {
|
|
| 512 |
+ out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+name+"/"+elem.Tag))
|
|
| 513 |
+ if err := r.PushRegistryTag(name, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
|
|
| 514 | 514 |
return err |
| 515 | 515 |
} |
| 516 | 516 |
} |
| 517 | 517 |
} |
| 518 | 518 |
|
| 519 |
- if _, err := r.PushImageJsonIndex(name, imgList, true); err != nil {
|
|
| 519 |
+ if _, err := r.PushImageJSONIndex(name, imgList, true); err != nil {
|
|
| 520 | 520 |
return err |
| 521 | 521 |
} |
| 522 | 522 |
return nil |
| 523 | 523 |
} |
| 524 | 524 |
|
| 525 |
-func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, ep string, token []string) error {
|
|
| 525 |
+func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, ep string, token []string, sf *utils.StreamFormatter) error {
|
|
| 526 | 526 |
out = utils.NewWriteFlusher(out) |
| 527 | 527 |
jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgId, "json")) |
| 528 | 528 |
if err != nil {
|
| 529 | 529 |
return fmt.Errorf("Error while retreiving the path for {%s}: %s", imgId, err)
|
| 530 | 530 |
} |
| 531 |
- fmt.Fprintf(out, "Pushing %s\r\n", imgId) |
|
| 531 |
+ out.Write(sf.FormatStatus("Pushing %s", imgId))
|
|
| 532 | 532 |
|
| 533 | 533 |
// Make sure we have the image's checksum |
| 534 | 534 |
checksum, err := srv.getChecksum(imgId) |
| ... | ... |
@@ -536,14 +539,14 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, |
| 536 | 536 |
return err |
| 537 | 537 |
} |
| 538 | 538 |
imgData := ®istry.ImgData{
|
| 539 |
- Id: imgId, |
|
| 539 |
+ ID: imgId, |
|
| 540 | 540 |
Checksum: checksum, |
| 541 | 541 |
} |
| 542 | 542 |
|
| 543 | 543 |
// Send the json |
| 544 |
- if err := r.PushImageJsonRegistry(imgData, jsonRaw, ep, token); err != nil {
|
|
| 544 |
+ if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep, token); err != nil {
|
|
| 545 | 545 |
if err == registry.ErrAlreadyExists {
|
| 546 |
- fmt.Fprintf(out, "Image %s already uploaded ; skipping\n", imgData.Id) |
|
| 546 |
+ out.Write(sf.FormatStatus("Image %s already uploaded ; skipping", imgData.ID))
|
|
| 547 | 547 |
return nil |
| 548 | 548 |
} |
| 549 | 549 |
return err |
| ... | ... |
@@ -576,22 +579,22 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, |
| 576 | 576 |
} |
| 577 | 577 |
|
| 578 | 578 |
// Send the layer |
| 579 |
- if err := r.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, "", false), ep, token); err != nil {
|
|
| 579 |
+ if err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("Pushing", "%v/%v (%v)"), sf), ep, token); err != nil {
|
|
| 580 | 580 |
return err |
| 581 | 581 |
} |
| 582 | 582 |
return nil |
| 583 | 583 |
} |
| 584 | 584 |
|
| 585 |
-func (srv *Server) ImagePush(name, endpoint string, out io.Writer) error {
|
|
| 585 |
+func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
|
|
| 586 | 586 |
out = utils.NewWriteFlusher(out) |
| 587 | 587 |
img, err := srv.runtime.graph.Get(name) |
| 588 | 588 |
r := registry.NewRegistry(srv.runtime.root) |
| 589 | 589 |
|
| 590 | 590 |
if err != nil {
|
| 591 |
- fmt.Fprintf(out, "The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) |
|
| 591 |
+ out.Write(sf.FormatStatus("The push refers to a repository [%s] (len: %d)", name, len(srv.runtime.repositories.Repositories[name])))
|
|
| 592 | 592 |
// If it fails, try to get the repository |
| 593 | 593 |
if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists {
|
| 594 |
- if err := srv.pushRepository(r, out, name, localRepo); err != nil {
|
|
| 594 |
+ if err := srv.pushRepository(r, out, name, localRepo, sf); err != nil {
|
|
| 595 | 595 |
return err |
| 596 | 596 |
} |
| 597 | 597 |
return nil |
| ... | ... |
@@ -599,14 +602,14 @@ func (srv *Server) ImagePush(name, endpoint string, out io.Writer) error {
|
| 599 | 599 |
|
| 600 | 600 |
return err |
| 601 | 601 |
} |
| 602 |
- fmt.Fprintf(out, "The push refers to an image: [%s]\n", name) |
|
| 603 |
- if err := srv.pushImage(r, out, name, img.Id, endpoint, nil); err != nil {
|
|
| 602 |
+ out.Write(sf.FormatStatus("The push refers to an image: [%s]", name))
|
|
| 603 |
+ if err := srv.pushImage(r, out, name, img.ID, endpoint, nil, sf); err != nil {
|
|
| 604 | 604 |
return err |
| 605 | 605 |
} |
| 606 | 606 |
return nil |
| 607 | 607 |
} |
| 608 | 608 |
|
| 609 |
-func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Writer) error {
|
|
| 609 |
+func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Writer, sf *utils.StreamFormatter) error {
|
|
| 610 | 610 |
var archive io.Reader |
| 611 | 611 |
var resp *http.Response |
| 612 | 612 |
|
| ... | ... |
@@ -615,21 +618,21 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write |
| 615 | 615 |
} else {
|
| 616 | 616 |
u, err := url.Parse(src) |
| 617 | 617 |
if err != nil {
|
| 618 |
- fmt.Fprintf(out, "Error: %s\n", err) |
|
| 618 |
+ return err |
|
| 619 | 619 |
} |
| 620 | 620 |
if u.Scheme == "" {
|
| 621 | 621 |
u.Scheme = "http" |
| 622 | 622 |
u.Host = src |
| 623 | 623 |
u.Path = "" |
| 624 | 624 |
} |
| 625 |
- fmt.Fprintf(out, "Downloading from %s\n", u) |
|
| 625 |
+ out.Write(sf.FormatStatus("Downloading from %s", u))
|
|
| 626 | 626 |
// Download with curl (pretty progress bar) |
| 627 | 627 |
// If curl is not available, fallback to http.Get() |
| 628 | 628 |
resp, err = utils.Download(u.String(), out) |
| 629 | 629 |
if err != nil {
|
| 630 | 630 |
return err |
| 631 | 631 |
} |
| 632 |
- archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)\r", false) |
|
| 632 |
+ archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("Importing", "%v/%v (%v)"), sf)
|
|
| 633 | 633 |
} |
| 634 | 634 |
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil) |
| 635 | 635 |
if err != nil {
|
| ... | ... |
@@ -637,11 +640,11 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write |
| 637 | 637 |
} |
| 638 | 638 |
// Optionally register the image at REPO/TAG |
| 639 | 639 |
if repo != "" {
|
| 640 |
- if err := srv.runtime.repositories.Set(repo, tag, img.Id, true); err != nil {
|
|
| 640 |
+ if err := srv.runtime.repositories.Set(repo, tag, img.ID, true); err != nil {
|
|
| 641 | 641 |
return err |
| 642 | 642 |
} |
| 643 | 643 |
} |
| 644 |
- fmt.Fprintf(out, "%s\n", img.ShortId()) |
|
| 644 |
+ out.Write(sf.FormatStatus(img.ShortID())) |
|
| 645 | 645 |
return nil |
| 646 | 646 |
} |
| 647 | 647 |
|
| ... | ... |
@@ -662,7 +665,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
|
| 662 | 662 |
} |
| 663 | 663 |
return "", err |
| 664 | 664 |
} |
| 665 |
- return container.ShortId(), nil |
|
| 665 |
+ return container.ShortID(), nil |
|
| 666 | 666 |
} |
| 667 | 667 |
|
| 668 | 668 |
func (srv *Server) ContainerRestart(name string, t int) error {
|
| ... | ... |
@@ -699,7 +702,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
|
| 699 | 699 |
for volumeId := range volumes {
|
| 700 | 700 |
// If the requested volu |
| 701 | 701 |
if c, exists := usedVolumes[volumeId]; exists {
|
| 702 |
- log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.Id)
|
|
| 702 |
+ log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.ID)
|
|
| 703 | 703 |
continue |
| 704 | 704 |
} |
| 705 | 705 |
if err := srv.runtime.volumes.Delete(volumeId); err != nil {
|
| ... | ... |
@@ -717,10 +720,9 @@ func (srv *Server) ImageDelete(name string) error {
|
| 717 | 717 |
img, err := srv.runtime.repositories.LookupImage(name) |
| 718 | 718 |
if err != nil {
|
| 719 | 719 |
return fmt.Errorf("No such image: %s", name)
|
| 720 |
- } else {
|
|
| 721 |
- if err := srv.runtime.graph.Delete(img.Id); err != nil {
|
|
| 722 |
- return fmt.Errorf("Error deleting image %s: %s", name, err.Error())
|
|
| 723 |
- } |
|
| 720 |
+ } |
|
| 721 |
+ if err := srv.runtime.graph.Delete(img.ID); err != nil {
|
|
| 722 |
+ return fmt.Errorf("Error deleting image %s: %s", name, err.Error())
|
|
| 724 | 723 |
} |
| 725 | 724 |
return nil |
| 726 | 725 |
} |
| ... | ... |
@@ -739,7 +741,7 @@ func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error) |
| 739 | 739 |
if _, exists := imageMap[img.Parent]; !exists {
|
| 740 | 740 |
imageMap[img.Parent] = make(map[string]struct{})
|
| 741 | 741 |
} |
| 742 |
- imageMap[img.Parent][img.Id] = struct{}{}
|
|
| 742 |
+ imageMap[img.Parent][img.ID] = struct{}{}
|
|
| 743 | 743 |
} |
| 744 | 744 |
|
| 745 | 745 |
// Loop on the children of the given image and check the config |
| ... | ... |
@@ -796,7 +798,6 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std |
| 796 | 796 |
if container == nil {
|
| 797 | 797 |
return fmt.Errorf("No such container: %s", name)
|
| 798 | 798 |
} |
| 799 |
- |
|
| 800 | 799 |
//logs |
| 801 | 800 |
if logs {
|
| 802 | 801 |
if stdout {
|
| ... | ... |
@@ -822,6 +823,9 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std |
| 822 | 822 |
if container.State.Ghost {
|
| 823 | 823 |
return fmt.Errorf("Impossible to attach to a ghost container")
|
| 824 | 824 |
} |
| 825 |
+ if !container.State.Running {
|
|
| 826 |
+ return fmt.Errorf("Impossible to attach to a stopped container, start it first")
|
|
| 827 |
+ } |
|
| 825 | 828 |
|
| 826 | 829 |
var ( |
| 827 | 830 |
cStdin io.ReadCloser |
| ... | ... |
@@ -13,7 +13,7 @@ func TestCreateRm(t *testing.T) {
|
| 13 | 13 |
|
| 14 | 14 |
srv := &Server{runtime: runtime}
|
| 15 | 15 |
|
| 16 |
- config, _, err := ParseRun([]string{GetTestImage(runtime).Id, "echo test"}, nil)
|
|
| 16 |
+ config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
|
|
| 17 | 17 |
if err != nil {
|
| 18 | 18 |
t.Fatal(err) |
| 19 | 19 |
} |
| ... | ... |
@@ -46,7 +46,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
|
| 46 | 46 |
|
| 47 | 47 |
srv := &Server{runtime: runtime}
|
| 48 | 48 |
|
| 49 |
- config, _, err := ParseRun([]string{GetTestImage(runtime).Id, "/bin/cat"}, nil)
|
|
| 49 |
+ config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "/bin/cat"}, nil)
|
|
| 50 | 50 |
if err != nil {
|
| 51 | 51 |
t.Fatal(err) |
| 52 | 52 |
} |
| ... | ... |
@@ -11,7 +11,7 @@ import ( |
| 11 | 11 |
"strings" |
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 |
-const DEFAULT_TAG = "latest" |
|
| 14 |
+const DEFAULTTAG = "latest" |
|
| 15 | 15 |
|
| 16 | 16 |
type TagStore struct {
|
| 17 | 17 |
path string |
| ... | ... |
@@ -72,7 +72,7 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
|
| 72 | 72 |
// (so we can pass all errors here) |
| 73 | 73 |
repoAndTag := strings.SplitN(name, ":", 2) |
| 74 | 74 |
if len(repoAndTag) == 1 {
|
| 75 |
- repoAndTag = append(repoAndTag, DEFAULT_TAG) |
|
| 75 |
+ repoAndTag = append(repoAndTag, DEFAULTTAG) |
|
| 76 | 76 |
} |
| 77 | 77 |
if i, err := store.GetImage(repoAndTag[0], repoAndTag[1]); err != nil {
|
| 78 | 78 |
return nil, err |
| ... | ... |
@@ -87,27 +87,27 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
|
| 87 | 87 |
|
| 88 | 88 |
// Return a reverse-lookup table of all the names which refer to each image |
| 89 | 89 |
// Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
|
| 90 |
-func (store *TagStore) ById() map[string][]string {
|
|
| 91 |
- byId := make(map[string][]string) |
|
| 90 |
+func (store *TagStore) ByID() map[string][]string {
|
|
| 91 |
+ byID := make(map[string][]string) |
|
| 92 | 92 |
for repoName, repository := range store.Repositories {
|
| 93 | 93 |
for tag, id := range repository {
|
| 94 | 94 |
name := repoName + ":" + tag |
| 95 |
- if _, exists := byId[id]; !exists {
|
|
| 96 |
- byId[id] = []string{name}
|
|
| 95 |
+ if _, exists := byID[id]; !exists {
|
|
| 96 |
+ byID[id] = []string{name}
|
|
| 97 | 97 |
} else {
|
| 98 |
- byId[id] = append(byId[id], name) |
|
| 99 |
- sort.Strings(byId[id]) |
|
| 98 |
+ byID[id] = append(byID[id], name) |
|
| 99 |
+ sort.Strings(byID[id]) |
|
| 100 | 100 |
} |
| 101 | 101 |
} |
| 102 | 102 |
} |
| 103 |
- return byId |
|
| 103 |
+ return byID |
|
| 104 | 104 |
} |
| 105 | 105 |
|
| 106 | 106 |
func (store *TagStore) ImageName(id string) string {
|
| 107 |
- if names, exists := store.ById()[id]; exists && len(names) > 0 {
|
|
| 107 |
+ if names, exists := store.ByID()[id]; exists && len(names) > 0 {
|
|
| 108 | 108 |
return names[0] |
| 109 | 109 |
} |
| 110 |
- return utils.TruncateId(id) |
|
| 110 |
+ return utils.TruncateID(id) |
|
| 111 | 111 |
} |
| 112 | 112 |
|
| 113 | 113 |
func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
|
| ... | ... |
@@ -116,7 +116,7 @@ func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
|
| 116 | 116 |
return err |
| 117 | 117 |
} |
| 118 | 118 |
if tag == "" {
|
| 119 |
- tag = DEFAULT_TAG |
|
| 119 |
+ tag = DEFAULTTAG |
|
| 120 | 120 |
} |
| 121 | 121 |
if err := validateRepoName(repoName); err != nil {
|
| 122 | 122 |
return err |
| ... | ... |
@@ -137,7 +137,7 @@ func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
|
| 137 | 137 |
} |
| 138 | 138 |
store.Repositories[repoName] = repo |
| 139 | 139 |
} |
| 140 |
- repo[tag] = img.Id |
|
| 140 |
+ repo[tag] = img.ID |
|
| 141 | 141 |
return store.Save() |
| 142 | 142 |
} |
| 143 | 143 |
|
| ... | ... |
@@ -7,104 +7,6 @@ import ( |
| 7 | 7 |
"unsafe" |
| 8 | 8 |
) |
| 9 | 9 |
|
| 10 |
-type Termios struct {
|
|
| 11 |
- Iflag uintptr |
|
| 12 |
- Oflag uintptr |
|
| 13 |
- Cflag uintptr |
|
| 14 |
- Lflag uintptr |
|
| 15 |
- Cc [20]byte |
|
| 16 |
- Ispeed uintptr |
|
| 17 |
- Ospeed uintptr |
|
| 18 |
-} |
|
| 19 |
- |
|
| 20 |
-const ( |
|
| 21 |
- // Input flags |
|
| 22 |
- inpck = 0x010 |
|
| 23 |
- istrip = 0x020 |
|
| 24 |
- icrnl = 0x100 |
|
| 25 |
- ixon = 0x200 |
|
| 26 |
- |
|
| 27 |
- // Output flags |
|
| 28 |
- opost = 0x1 |
|
| 29 |
- |
|
| 30 |
- // Control flags |
|
| 31 |
- cs8 = 0x300 |
|
| 32 |
- |
|
| 33 |
- // Local flags |
|
| 34 |
- icanon = 0x100 |
|
| 35 |
- iexten = 0x400 |
|
| 36 |
-) |
|
| 37 |
- |
|
| 38 |
-const ( |
|
| 39 |
- HUPCL = 0x4000 |
|
| 40 |
- ICANON = 0x100 |
|
| 41 |
- ICRNL = 0x100 |
|
| 42 |
- IEXTEN = 0x400 |
|
| 43 |
- BRKINT = 0x2 |
|
| 44 |
- CFLUSH = 0xf |
|
| 45 |
- CLOCAL = 0x8000 |
|
| 46 |
- CREAD = 0x800 |
|
| 47 |
- CS5 = 0x0 |
|
| 48 |
- CS6 = 0x100 |
|
| 49 |
- CS7 = 0x200 |
|
| 50 |
- CS8 = 0x300 |
|
| 51 |
- CSIZE = 0x300 |
|
| 52 |
- CSTART = 0x11 |
|
| 53 |
- CSTATUS = 0x14 |
|
| 54 |
- CSTOP = 0x13 |
|
| 55 |
- CSTOPB = 0x400 |
|
| 56 |
- CSUSP = 0x1a |
|
| 57 |
- IGNBRK = 0x1 |
|
| 58 |
- IGNCR = 0x80 |
|
| 59 |
- IGNPAR = 0x4 |
|
| 60 |
- IMAXBEL = 0x2000 |
|
| 61 |
- INLCR = 0x40 |
|
| 62 |
- INPCK = 0x10 |
|
| 63 |
- ISIG = 0x80 |
|
| 64 |
- ISTRIP = 0x20 |
|
| 65 |
- IUTF8 = 0x4000 |
|
| 66 |
- IXANY = 0x800 |
|
| 67 |
- IXOFF = 0x400 |
|
| 68 |
- IXON = 0x200 |
|
| 69 |
- NOFLSH = 0x80000000 |
|
| 70 |
- OCRNL = 0x10 |
|
| 71 |
- OFDEL = 0x20000 |
|
| 72 |
- OFILL = 0x80 |
|
| 73 |
- ONLCR = 0x2 |
|
| 74 |
- ONLRET = 0x40 |
|
| 75 |
- ONOCR = 0x20 |
|
| 76 |
- ONOEOT = 0x8 |
|
| 77 |
- OPOST = 0x1 |
|
| 78 |
- RENB = 0x1000 |
|
| 79 |
- PARMRK = 0x8 |
|
| 80 |
- PARODD = 0x2000 |
|
| 81 |
- |
|
| 82 |
- TOSTOP = 0x400000 |
|
| 83 |
- VDISCARD = 0xf |
|
| 84 |
- VDSUSP = 0xb |
|
| 85 |
- VEOF = 0x0 |
|
| 86 |
- VEOL = 0x1 |
|
| 87 |
- VEOL2 = 0x2 |
|
| 88 |
- VERASE = 0x3 |
|
| 89 |
- VINTR = 0x8 |
|
| 90 |
- VKILL = 0x5 |
|
| 91 |
- VLNEXT = 0xe |
|
| 92 |
- VMIN = 0x10 |
|
| 93 |
- VQUIT = 0x9 |
|
| 94 |
- VREPRINT = 0x6 |
|
| 95 |
- VSTART = 0xc |
|
| 96 |
- VSTATUS = 0x12 |
|
| 97 |
- VSTOP = 0xd |
|
| 98 |
- VSUSP = 0xa |
|
| 99 |
- VT0 = 0x0 |
|
| 100 |
- VT1 = 0x10000 |
|
| 101 |
- VTDLY = 0x10000 |
|
| 102 |
- VTIME = 0x11 |
|
| 103 |
- ECHO = 0x00000008 |
|
| 104 |
- |
|
| 105 |
- PENDIN = 0x20000000 |
|
| 106 |
-) |
|
| 107 |
- |
|
| 108 | 10 |
type State struct {
|
| 109 | 11 |
termios Termios |
| 110 | 12 |
} |
| ... | ... |
@@ -128,21 +30,21 @@ func SetWinsize(fd uintptr, ws *Winsize) error {
|
| 128 | 128 |
} |
| 129 | 129 |
|
| 130 | 130 |
// IsTerminal returns true if the given file descriptor is a terminal. |
| 131 |
-func IsTerminal(fd int) bool {
|
|
| 131 |
+func IsTerminal(fd uintptr) bool {
|
|
| 132 | 132 |
var termios Termios |
| 133 |
- _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios))) |
|
| 133 |
+ _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&termios))) |
|
| 134 | 134 |
return err == 0 |
| 135 | 135 |
} |
| 136 | 136 |
|
| 137 | 137 |
// Restore restores the terminal connected to the given file descriptor to a |
| 138 | 138 |
// previous state. |
| 139 |
-func Restore(fd int, state *State) error {
|
|
| 140 |
- _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios))) |
|
| 139 |
+func Restore(fd uintptr, state *State) error {
|
|
| 140 |
+ _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios))) |
|
| 141 | 141 |
return err |
| 142 | 142 |
} |
| 143 | 143 |
|
| 144 | 144 |
func SetRawTerminal() (*State, error) {
|
| 145 |
- oldState, err := MakeRaw(int(os.Stdin.Fd())) |
|
| 145 |
+ oldState, err := MakeRaw(os.Stdin.Fd()) |
|
| 146 | 146 |
if err != nil {
|
| 147 | 147 |
return nil, err |
| 148 | 148 |
} |
| ... | ... |
@@ -150,12 +52,12 @@ func SetRawTerminal() (*State, error) {
|
| 150 | 150 |
signal.Notify(c, os.Interrupt) |
| 151 | 151 |
go func() {
|
| 152 | 152 |
_ = <-c |
| 153 |
- Restore(int(os.Stdin.Fd()), oldState) |
|
| 153 |
+ Restore(os.Stdin.Fd(), oldState) |
|
| 154 | 154 |
os.Exit(0) |
| 155 | 155 |
}() |
| 156 | 156 |
return oldState, err |
| 157 | 157 |
} |
| 158 | 158 |
|
| 159 | 159 |
func RestoreTerminal(state *State) {
|
| 160 |
- Restore(int(os.Stdin.Fd()), state) |
|
| 160 |
+ Restore(os.Stdin.Fd(), state) |
|
| 161 | 161 |
} |
| ... | ... |
@@ -8,23 +8,45 @@ import ( |
| 8 | 8 |
const ( |
| 9 | 9 |
getTermios = syscall.TIOCGETA |
| 10 | 10 |
setTermios = syscall.TIOCSETA |
| 11 |
+ |
|
| 12 |
+ ECHO = 0x00000008 |
|
| 13 |
+ ONLCR = 0x2 |
|
| 14 |
+ ISTRIP = 0x20 |
|
| 15 |
+ INLCR = 0x40 |
|
| 16 |
+ ISIG = 0x80 |
|
| 17 |
+ IGNCR = 0x80 |
|
| 18 |
+ ICANON = 0x100 |
|
| 19 |
+ ICRNL = 0x100 |
|
| 20 |
+ IXOFF = 0x400 |
|
| 21 |
+ IXON = 0x200 |
|
| 11 | 22 |
) |
| 12 | 23 |
|
| 24 |
+type Termios struct {
|
|
| 25 |
+ Iflag uint64 |
|
| 26 |
+ Oflag uint64 |
|
| 27 |
+ Cflag uint64 |
|
| 28 |
+ Lflag uint64 |
|
| 29 |
+ Cc [20]byte |
|
| 30 |
+ Ispeed uint64 |
|
| 31 |
+ Ospeed uint64 |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 13 | 34 |
// MakeRaw put the terminal connected to the given file descriptor into raw |
| 14 | 35 |
// mode and returns the previous state of the terminal so that it can be |
| 15 | 36 |
// restored. |
| 16 |
-func MakeRaw(fd int) (*State, error) {
|
|
| 37 |
+func MakeRaw(fd uintptr) (*State, error) {
|
|
| 17 | 38 |
var oldState State |
| 18 |
- if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
|
| 39 |
+ if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
|
| 19 | 40 |
return nil, err |
| 20 | 41 |
} |
| 21 | 42 |
|
| 22 | 43 |
newState := oldState.termios |
| 23 |
- newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF |
|
| 44 |
+ newState.Iflag &^= (ISTRIP | INLCR | IGNCR | IXON | IXOFF) |
|
| 24 | 45 |
newState.Iflag |= ICRNL |
| 25 | 46 |
newState.Oflag |= ONLCR |
| 26 |
- newState.Lflag &^= ECHO | ICANON | ISIG |
|
| 27 |
- if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
|
| 47 |
+ newState.Lflag &^= (ECHO | ICANON | ISIG) |
|
| 48 |
+ |
|
| 49 |
+ if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
|
| 28 | 50 |
return nil, err |
| 29 | 51 |
} |
| 30 | 52 |
|
| ... | ... |
@@ -5,54 +5,40 @@ import ( |
| 5 | 5 |
"unsafe" |
| 6 | 6 |
) |
| 7 | 7 |
|
| 8 |
-// #include <termios.h> |
|
| 9 |
-// #include <sys/ioctl.h> |
|
| 10 |
-/* |
|
| 11 |
-void MakeRaw(int fd) {
|
|
| 12 |
- struct termios t; |
|
| 13 |
- |
|
| 14 |
- // FIXME: Handle errors? |
|
| 15 |
- ioctl(fd, TCGETS, &t); |
|
| 16 |
- |
|
| 17 |
- t.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); |
|
| 18 |
- t.c_oflag &= ~OPOST; |
|
| 19 |
- t.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); |
|
| 20 |
- t.c_cflag &= ~(CSIZE | PARENB); |
|
| 21 |
- t.c_cflag |= CS8; |
|
| 22 |
- |
|
| 23 |
- ioctl(fd, TCSETS, &t); |
|
| 24 |
-} |
|
| 25 |
-*/ |
|
| 26 |
-import "C" |
|
| 27 |
- |
|
| 28 | 8 |
const ( |
| 29 | 9 |
getTermios = syscall.TCGETS |
| 30 | 10 |
setTermios = syscall.TCSETS |
| 31 | 11 |
) |
| 32 | 12 |
|
| 13 |
+type Termios struct {
|
|
| 14 |
+ Iflag uint32 |
|
| 15 |
+ Oflag uint32 |
|
| 16 |
+ Cflag uint32 |
|
| 17 |
+ Lflag uint32 |
|
| 18 |
+ Cc [20]byte |
|
| 19 |
+ Ispeed uint32 |
|
| 20 |
+ Ospeed uint32 |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 33 | 23 |
// MakeRaw put the terminal connected to the given file descriptor into raw |
| 34 | 24 |
// mode and returns the previous state of the terminal so that it can be |
| 35 | 25 |
// restored. |
| 36 |
-func MakeRaw(fd int) (*State, error) {
|
|
| 26 |
+func MakeRaw(fd uintptr) (*State, error) {
|
|
| 37 | 27 |
var oldState State |
| 38 |
- if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TCGETS, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
|
| 28 |
+ if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
|
| 39 | 29 |
return nil, err |
| 40 | 30 |
} |
| 41 |
- C.MakeRaw(C.int(fd)) |
|
| 42 |
- return &oldState, nil |
|
| 43 |
- |
|
| 44 |
- // FIXME: post on goland issues this: very same as the C function bug non-working |
|
| 45 | 31 |
|
| 46 |
- // newState := oldState.termios |
|
| 32 |
+ newState := oldState.termios |
|
| 47 | 33 |
|
| 48 |
- // newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON) |
|
| 49 |
- // newState.Oflag &^= OPOST |
|
| 50 |
- // newState.Lflag &^= (ECHO | syscall.ECHONL | ICANON | ISIG | IEXTEN) |
|
| 51 |
- // newState.Cflag &^= (CSIZE | syscall.PARENB) |
|
| 52 |
- // newState.Cflag |= CS8 |
|
| 34 |
+ newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON) |
|
| 35 |
+ newState.Oflag &^= syscall.OPOST |
|
| 36 |
+ newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN) |
|
| 37 |
+ newState.Cflag &^= (syscall.CSIZE | syscall.PARENB) |
|
| 38 |
+ newState.Cflag |= syscall.CS8 |
|
| 53 | 39 |
|
| 54 |
- // if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TCSETS, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
|
| 55 |
- // return nil, err |
|
| 56 |
- // } |
|
| 57 |
- // return &oldState, nil |
|
| 40 |
+ if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
|
| 41 |
+ return nil, err |
|
| 42 |
+ } |
|
| 43 |
+ return &oldState, nil |
|
| 58 | 44 |
} |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"bytes" |
| 5 | 5 |
"crypto/sha256" |
| 6 | 6 |
"encoding/hex" |
| 7 |
+ "encoding/json" |
|
| 7 | 8 |
"errors" |
| 8 | 9 |
"fmt" |
| 9 | 10 |
"index/suffixarray" |
| ... | ... |
@@ -33,7 +34,7 @@ func Go(f func() error) chan error {
|
| 33 | 33 |
// Request a given URL and return an io.Reader |
| 34 | 34 |
func Download(url string, stderr io.Writer) (*http.Response, error) {
|
| 35 | 35 |
var resp *http.Response |
| 36 |
- var err error = nil |
|
| 36 |
+ var err error |
|
| 37 | 37 |
if resp, err = http.Get(url); err != nil {
|
| 38 | 38 |
return nil, err |
| 39 | 39 |
} |
| ... | ... |
@@ -69,7 +70,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 |
+ sf *StreamFormatter |
|
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 | 75 |
func (r *progressReader) Read(p []byte) (n int, err error) {
|
| ... | ... |
@@ -93,7 +94,7 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
|
| 93 | 93 |
} |
| 94 | 94 |
// Send newline when complete |
| 95 | 95 |
if err != nil {
|
| 96 |
- fmt.Fprintf(r.output, FormatStatus("", r.json))
|
|
| 96 |
+ r.output.Write(r.sf.FormatStatus(""))
|
|
| 97 | 97 |
} |
| 98 | 98 |
|
| 99 | 99 |
return read, err |
| ... | ... |
@@ -101,11 +102,12 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
|
| 101 | 101 |
func (r *progressReader) Close() error {
|
| 102 | 102 |
return io.ReadCloser(r.reader).Close() |
| 103 | 103 |
} |
| 104 |
-func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string, json bool) *progressReader {
|
|
| 105 |
- if template == "" {
|
|
| 106 |
- template = "%v/%v (%v)\r" |
|
| 104 |
+func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte, sf *StreamFormatter) *progressReader {
|
|
| 105 |
+ tpl := string(template) |
|
| 106 |
+ if tpl == "" {
|
|
| 107 |
+ tpl = string(sf.FormatProgress("", "%v/%v (%v)"))
|
|
| 107 | 108 |
} |
| 108 |
- return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template, json}
|
|
| 109 |
+ return &progressReader{r, NewWriteFlusher(output), size, 0, 0, tpl, sf}
|
|
| 109 | 110 |
} |
| 110 | 111 |
|
| 111 | 112 |
// HumanDuration returns a human-readable approximation of a duration |
| ... | ... |
@@ -361,11 +363,11 @@ func (idx *TruncIndex) Get(s string) (string, error) {
|
| 361 | 361 |
return string(idx.bytes[before:after]), err |
| 362 | 362 |
} |
| 363 | 363 |
|
| 364 |
-// TruncateId returns a shorthand version of a string identifier for convenience. |
|
| 364 |
+// TruncateID returns a shorthand version of a string identifier for convenience. |
|
| 365 | 365 |
// A collision with other shorthands is very unlikely, but possible. |
| 366 | 366 |
// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller |
| 367 | 367 |
// will need to use a langer prefix, or the full-length Id. |
| 368 |
-func TruncateId(id string) string {
|
|
| 368 |
+func TruncateID(id string) string {
|
|
| 369 | 369 |
shortLen := 12 |
| 370 | 370 |
if len(id) < shortLen {
|
| 371 | 371 |
shortLen = len(id) |
| ... | ... |
@@ -578,16 +580,57 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
|
| 578 | 578 |
return &WriteFlusher{w: w, flusher: flusher}
|
| 579 | 579 |
} |
| 580 | 580 |
|
| 581 |
-func FormatStatus(str string, json bool) string {
|
|
| 582 |
- if json {
|
|
| 583 |
- return "{\"status\" : \"" + str + "\"}"
|
|
| 581 |
+type JSONMessage struct {
|
|
| 582 |
+ Status string `json:"status,omitempty"` |
|
| 583 |
+ Progress string `json:"progress,omitempty"` |
|
| 584 |
+ Error string `json:"error,omitempty"` |
|
| 585 |
+} |
|
| 586 |
+ |
|
| 587 |
+type StreamFormatter struct {
|
|
| 588 |
+ json bool |
|
| 589 |
+ used bool |
|
| 590 |
+} |
|
| 591 |
+ |
|
| 592 |
+func NewStreamFormatter(json bool) *StreamFormatter {
|
|
| 593 |
+ return &StreamFormatter{json, false}
|
|
| 594 |
+} |
|
| 595 |
+ |
|
| 596 |
+func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte {
|
|
| 597 |
+ sf.used = true |
|
| 598 |
+ str := fmt.Sprintf(format, a...) |
|
| 599 |
+ if sf.json {
|
|
| 600 |
+ b, err := json.Marshal(&JSONMessage{Status:str});
|
|
| 601 |
+ if err != nil {
|
|
| 602 |
+ return sf.FormatError(err) |
|
| 603 |
+ } |
|
| 604 |
+ return b |
|
| 584 | 605 |
} |
| 585 |
- return str + "\r\n" |
|
| 606 |
+ return []byte(str + "\r\n") |
|
| 586 | 607 |
} |
| 587 | 608 |
|
| 588 |
-func FormatProgress(str string, json bool) string {
|
|
| 589 |
- if json {
|
|
| 590 |
- return "{\"progress\" : \"" + str + "\"}"
|
|
| 609 |
+func (sf *StreamFormatter) FormatError(err error) []byte {
|
|
| 610 |
+ sf.used = true |
|
| 611 |
+ if sf.json {
|
|
| 612 |
+ if b, err := json.Marshal(&JSONMessage{Error:err.Error()}); err == nil {
|
|
| 613 |
+ return b |
|
| 614 |
+ } |
|
| 615 |
+ return []byte("{\"error\":\"format error\"}")
|
|
| 591 | 616 |
} |
| 592 |
- return "Downloading " + str + "\r" |
|
| 617 |
+ return []byte("Error: " + err.Error() + "\r\n")
|
|
| 618 |
+} |
|
| 619 |
+ |
|
| 620 |
+func (sf *StreamFormatter) FormatProgress(action, str string) []byte {
|
|
| 621 |
+ sf.used = true |
|
| 622 |
+ if sf.json {
|
|
| 623 |
+ b, err := json.Marshal(&JSONMessage{Status: action, Progress:str})
|
|
| 624 |
+ if err != nil {
|
|
| 625 |
+ return nil |
|
| 626 |
+ } |
|
| 627 |
+ return b |
|
| 628 |
+ } |
|
| 629 |
+ return []byte(action + " " + str + "\r") |
|
| 630 |
+} |
|
| 631 |
+ |
|
| 632 |
+func (sf *StreamFormatter) Used() bool {
|
|
| 633 |
+ return sf.used |
|
| 593 | 634 |
} |