Browse code

Merge branch 'master' into new_logs

Victor Vieux authored on 2013/07/15 22:57:54
Showing 27 changed files
... ...
@@ -47,21 +47,22 @@ func parseMultipartForm(r *http.Request) error {
47 47
 }
48 48
 
49 49
 func httpError(w http.ResponseWriter, err error) {
50
+	statusCode := http.StatusInternalServerError
50 51
 	if strings.HasPrefix(err.Error(), "No such") {
51
-		http.Error(w, err.Error(), http.StatusNotFound)
52
+		statusCode = http.StatusNotFound
52 53
 	} else if strings.HasPrefix(err.Error(), "Bad parameter") {
53
-		http.Error(w, err.Error(), http.StatusBadRequest)
54
+		statusCode = http.StatusBadRequest
54 55
 	} else if strings.HasPrefix(err.Error(), "Conflict") {
55
-		http.Error(w, err.Error(), http.StatusConflict)
56
+		statusCode = http.StatusConflict
56 57
 	} else if strings.HasPrefix(err.Error(), "Impossible") {
57
-		http.Error(w, err.Error(), http.StatusNotAcceptable)
58
+		statusCode = http.StatusNotAcceptable
58 59
 	} else if strings.HasPrefix(err.Error(), "Wrong login/password") {
59
-		http.Error(w, err.Error(), http.StatusUnauthorized)
60
+		statusCode = http.StatusUnauthorized
60 61
 	} else if strings.Contains(err.Error(), "hasn't been activated") {
61
-		http.Error(w, err.Error(), http.StatusForbidden)
62
-	} else {
63
-		http.Error(w, err.Error(), http.StatusInternalServerError)
62
+		statusCode = http.StatusForbidden
64 63
 	}
64
+	utils.Debugf("[error %d] %s", statusCode, err)
65
+	http.Error(w, err.Error(), statusCode)
65 66
 }
66 67
 
67 68
 func writeJSON(w http.ResponseWriter, b []byte) {
... ...
@@ -250,6 +251,23 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r
250 250
 	return nil
251 251
 }
252 252
 
253
+func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
254
+	if vars == nil {
255
+		return fmt.Errorf("Missing parameter")
256
+	}
257
+	name := vars["name"]
258
+	procsStr, err := srv.ContainerTop(name)
259
+	if err != nil {
260
+		return err
261
+	}
262
+	b, err := json.Marshal(procsStr)
263
+	if err != nil {
264
+		return err
265
+	}
266
+	writeJSON(w, b)
267
+	return nil
268
+}
269
+
253 270
 func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
254 271
 	if err := parseForm(r); err != nil {
255 272
 		return err
... ...
@@ -842,6 +860,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
842 842
 			"/containers/{name:.*}/export":  getContainersExport,
843 843
 			"/containers/{name:.*}/changes": getContainersChanges,
844 844
 			"/containers/{name:.*}/json":    getContainersByName,
845
+			"/containers/{name:.*}/top":     getContainersTop,
845 846
 		},
846 847
 		"POST": {
847 848
 			"/auth":                         postAuth,
... ...
@@ -26,6 +26,13 @@ type APIInfo struct {
26 26
 	SwapLimit   bool `json:",omitempty"`
27 27
 }
28 28
 
29
+type APITop struct {
30
+	PID  string
31
+	Tty  string
32
+	Time string
33
+	Cmd  string
34
+}
35
+
29 36
 type APIRmi struct {
30 37
 	Deleted  string `json:",omitempty"`
31 38
 	Untagged string `json:",omitempty"`
... ...
@@ -41,10 +41,8 @@ func TestGetBoolParam(t *testing.T) {
41 41
 }
42 42
 
43 43
 func TestGetVersion(t *testing.T) {
44
-	runtime, err := newTestRuntime()
45
-	if err != nil {
46
-		t.Fatal(err)
47
-	}
44
+	var err error
45
+	runtime := mkRuntime(t)
48 46
 	defer nuke(runtime)
49 47
 
50 48
 	srv := &Server{runtime: runtime}
... ...
@@ -65,10 +63,7 @@ func TestGetVersion(t *testing.T) {
65 65
 }
66 66
 
67 67
 func TestGetInfo(t *testing.T) {
68
-	runtime, err := newTestRuntime()
69
-	if err != nil {
70
-		t.Fatal(err)
71
-	}
68
+	runtime := mkRuntime(t)
72 69
 	defer nuke(runtime)
73 70
 
74 71
 	srv := &Server{runtime: runtime}
... ...
@@ -95,10 +90,7 @@ func TestGetInfo(t *testing.T) {
95 95
 }
96 96
 
97 97
 func TestGetImagesJSON(t *testing.T) {
98
-	runtime, err := newTestRuntime()
99
-	if err != nil {
100
-		t.Fatal(err)
101
-	}
98
+	runtime := mkRuntime(t)
102 99
 	defer nuke(runtime)
103 100
 
104 101
 	srv := &Server{runtime: runtime}
... ...
@@ -220,10 +212,7 @@ func TestGetImagesJSON(t *testing.T) {
220 220
 }
221 221
 
222 222
 func TestGetImagesViz(t *testing.T) {
223
-	runtime, err := newTestRuntime()
224
-	if err != nil {
225
-		t.Fatal(err)
226
-	}
223
+	runtime := mkRuntime(t)
227 224
 	defer nuke(runtime)
228 225
 
229 226
 	srv := &Server{runtime: runtime}
... ...
@@ -248,10 +237,7 @@ func TestGetImagesViz(t *testing.T) {
248 248
 }
249 249
 
250 250
 func TestGetImagesHistory(t *testing.T) {
251
-	runtime, err := newTestRuntime()
252
-	if err != nil {
253
-		t.Fatal(err)
254
-	}
251
+	runtime := mkRuntime(t)
255 252
 	defer nuke(runtime)
256 253
 
257 254
 	srv := &Server{runtime: runtime}
... ...
@@ -272,10 +258,7 @@ func TestGetImagesHistory(t *testing.T) {
272 272
 }
273 273
 
274 274
 func TestGetImagesByName(t *testing.T) {
275
-	runtime, err := newTestRuntime()
276
-	if err != nil {
277
-		t.Fatal(err)
278
-	}
275
+	runtime := mkRuntime(t)
279 276
 	defer nuke(runtime)
280 277
 
281 278
 	srv := &Server{runtime: runtime}
... ...
@@ -295,10 +278,7 @@ func TestGetImagesByName(t *testing.T) {
295 295
 }
296 296
 
297 297
 func TestGetContainersJSON(t *testing.T) {
298
-	runtime, err := newTestRuntime()
299
-	if err != nil {
300
-		t.Fatal(err)
301
-	}
298
+	runtime := mkRuntime(t)
302 299
 	defer nuke(runtime)
303 300
 
304 301
 	srv := &Server{runtime: runtime}
... ...
@@ -334,10 +314,7 @@ func TestGetContainersJSON(t *testing.T) {
334 334
 }
335 335
 
336 336
 func TestGetContainersExport(t *testing.T) {
337
-	runtime, err := newTestRuntime()
338
-	if err != nil {
339
-		t.Fatal(err)
340
-	}
337
+	runtime := mkRuntime(t)
341 338
 	defer nuke(runtime)
342 339
 
343 340
 	srv := &Server{runtime: runtime}
... ...
@@ -389,10 +366,7 @@ func TestGetContainersExport(t *testing.T) {
389 389
 }
390 390
 
391 391
 func TestGetContainersChanges(t *testing.T) {
392
-	runtime, err := newTestRuntime()
393
-	if err != nil {
394
-		t.Fatal(err)
395
-	}
392
+	runtime := mkRuntime(t)
396 393
 	defer nuke(runtime)
397 394
 
398 395
 	srv := &Server{runtime: runtime}
... ...
@@ -436,7 +410,7 @@ func TestGetContainersChanges(t *testing.T) {
436 436
 	}
437 437
 }
438 438
 
439
-func TestGetContainersByName(t *testing.T) {
439
+func TestGetContainersTop(t *testing.T) {
440 440
 	runtime, err := newTestRuntime()
441 441
 	if err != nil {
442 442
 		t.Fatal(err)
... ...
@@ -447,6 +421,58 @@ func TestGetContainersByName(t *testing.T) {
447 447
 
448 448
 	builder := NewBuilder(runtime)
449 449
 
450
+	container, err := builder.Create(
451
+		&Config{
452
+			Image: GetTestImage(runtime).ID,
453
+			Cmd:   []string{"/bin/sh", "-c", "sleep 2"},
454
+		},
455
+	)
456
+	if err != nil {
457
+		t.Fatal(err)
458
+	}
459
+	defer runtime.Destroy(container)
460
+	hostConfig := &HostConfig{}
461
+	if err := container.Start(hostConfig); err != nil {
462
+		t.Fatal(err)
463
+	}
464
+
465
+	// Give some time to the process to start
466
+	container.WaitTimeout(500 * time.Millisecond)
467
+
468
+	if !container.State.Running {
469
+		t.Errorf("Container should be running")
470
+	}
471
+
472
+	r := httptest.NewRecorder()
473
+	if err := getContainersTop(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
474
+		t.Fatal(err)
475
+	}
476
+	procs := []APITop{}
477
+	if err := json.Unmarshal(r.Body.Bytes(), &procs); err != nil {
478
+		t.Fatal(err)
479
+	}
480
+
481
+	if len(procs) != 2 {
482
+		t.Fatalf("Expected 2 processes, found %d.", len(procs))
483
+	}
484
+
485
+	if procs[0].Cmd != "sh" && procs[0].Cmd != "busybox" {
486
+		t.Fatalf("Expected `busybox` or `sh`, found %s.", procs[0].Cmd)
487
+	}
488
+
489
+	if procs[1].Cmd != "sh" && procs[1].Cmd != "busybox" {
490
+		t.Fatalf("Expected `busybox` or `sh`, found %s.", procs[1].Cmd)
491
+	}
492
+}
493
+
494
+func TestGetContainersByName(t *testing.T) {
495
+	runtime := mkRuntime(t)
496
+	defer nuke(runtime)
497
+
498
+	srv := &Server{runtime: runtime}
499
+
500
+	builder := NewBuilder(runtime)
501
+
450 502
 	// Create a container and remove a file
451 503
 	container, err := builder.Create(
452 504
 		&Config{
... ...
@@ -473,10 +499,7 @@ func TestGetContainersByName(t *testing.T) {
473 473
 }
474 474
 
475 475
 func TestPostCommit(t *testing.T) {
476
-	runtime, err := newTestRuntime()
477
-	if err != nil {
478
-		t.Fatal(err)
479
-	}
476
+	runtime := mkRuntime(t)
480 477
 	defer nuke(runtime)
481 478
 
482 479
 	srv := &Server{runtime: runtime}
... ...
@@ -521,249 +544,8 @@ func TestPostCommit(t *testing.T) {
521 521
 	}
522 522
 }
523 523
 
524
-func TestPostImagesCreate(t *testing.T) {
525
-	// FIXME: Use the staging in order to perform tests
526
-
527
-	// runtime, err := newTestRuntime()
528
-	// if err != nil {
529
-	// 	t.Fatal(err)
530
-	// }
531
-	// defer nuke(runtime)
532
-
533
-	// srv := &Server{runtime: runtime}
534
-
535
-	// stdin, stdinPipe := io.Pipe()
536
-	// stdout, stdoutPipe := io.Pipe()
537
-
538
-	// c1 := make(chan struct{})
539
-	// go func() {
540
-	// 	defer close(c1)
541
-
542
-	// 	r := &hijackTester{
543
-	// 		ResponseRecorder: httptest.NewRecorder(),
544
-	// 		in:               stdin,
545
-	// 		out:              stdoutPipe,
546
-	// 	}
547
-
548
-	// 	req, err := http.NewRequest("POST", "/images/create?fromImage="+unitTestImageName, bytes.NewReader([]byte{}))
549
-	// 	if err != nil {
550
-	// 		t.Fatal(err)
551
-	// 	}
552
-
553
-	// 	body, err := postImagesCreate(srv, r, req, nil)
554
-	// 	if err != nil {
555
-	// 		t.Fatal(err)
556
-	// 	}
557
-	// 	if body != nil {
558
-	// 		t.Fatalf("No body expected, received: %s\n", body)
559
-	// 	}
560
-	// }()
561
-
562
-	// // Acknowledge hijack
563
-	// setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() {
564
-	// 	stdout.Read([]byte{})
565
-	// 	stdout.Read(make([]byte, 4096))
566
-	// })
567
-
568
-	// setTimeout(t, "Waiting for imagesCreate output", 5*time.Second, func() {
569
-	// 	reader := bufio.NewReader(stdout)
570
-	// 	line, err := reader.ReadString('\n')
571
-	// 	if err != nil {
572
-	// 		t.Fatal(err)
573
-	// 	}
574
-	// 	if !strings.HasPrefix(line, "Pulling repository d from") {
575
-	// 		t.Fatalf("Expected Pulling repository docker-ut from..., found %s", line)
576
-	// 	}
577
-	// })
578
-
579
-	// // Close pipes (client disconnects)
580
-	// if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
581
-	// 	t.Fatal(err)
582
-	// }
583
-
584
-	// // Wait for imagesCreate to finish, the client disconnected, therefore, Create finished his job
585
-	// setTimeout(t, "Waiting for imagesCreate timed out", 10*time.Second, func() {
586
-	// 	<-c1
587
-	// })
588
-}
589
-
590
-func TestPostImagesInsert(t *testing.T) {
591
-	// runtime, err := newTestRuntime()
592
-	// if err != nil {
593
-	// 	t.Fatal(err)
594
-	// }
595
-	// defer nuke(runtime)
596
-
597
-	// srv := &Server{runtime: runtime}
598
-
599
-	// stdin, stdinPipe := io.Pipe()
600
-	// stdout, stdoutPipe := io.Pipe()
601
-
602
-	// // Attach to it
603
-	// c1 := make(chan struct{})
604
-	// go func() {
605
-	// 	defer close(c1)
606
-	// 	r := &hijackTester{
607
-	// 		ResponseRecorder: httptest.NewRecorder(),
608
-	// 		in:               stdin,
609
-	// 		out:              stdoutPipe,
610
-	// 	}
611
-
612
-	// 	req, err := http.NewRequest("POST", "/images/"+unitTestImageName+"/insert?path=%2Ftest&url=https%3A%2F%2Fraw.github.com%2Fdotcloud%2Fdocker%2Fmaster%2FREADME.md", bytes.NewReader([]byte{}))
613
-	// 	if err != nil {
614
-	// 		t.Fatal(err)
615
-	// 	}
616
-	// 	if err := postContainersCreate(srv, r, req, nil); err != nil {
617
-	// 		t.Fatal(err)
618
-	// 	}
619
-	// }()
620
-
621
-	// // Acknowledge hijack
622
-	// setTimeout(t, "hijack acknowledge timed out", 5*time.Second, func() {
623
-	// 	stdout.Read([]byte{})
624
-	// 	stdout.Read(make([]byte, 4096))
625
-	// })
626
-
627
-	// id := ""
628
-	// setTimeout(t, "Waiting for imagesInsert output", 10*time.Second, func() {
629
-	// 	for {
630
-	// 		reader := bufio.NewReader(stdout)
631
-	// 		id, err = reader.ReadString('\n')
632
-	// 		if err != nil {
633
-	// 			t.Fatal(err)
634
-	// 		}
635
-	// 	}
636
-	// })
637
-
638
-	// // Close pipes (client disconnects)
639
-	// if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
640
-	// 	t.Fatal(err)
641
-	// }
642
-
643
-	// // Wait for attach to finish, the client disconnected, therefore, Attach finished his job
644
-	// setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() {
645
-	// 	<-c1
646
-	// })
647
-
648
-	// img, err := srv.runtime.repositories.LookupImage(id)
649
-	// if err != nil {
650
-	// 	t.Fatalf("New image %s expected but not found", id)
651
-	// }
652
-
653
-	// layer, err := img.layer()
654
-	// if err != nil {
655
-	// 	t.Fatal(err)
656
-	// }
657
-
658
-	// if _, err := os.Stat(path.Join(layer, "test")); err != nil {
659
-	// 	t.Fatalf("The test file has not been found")
660
-	// }
661
-
662
-	// if err := srv.runtime.graph.Delete(img.ID); err != nil {
663
-	// 	t.Fatal(err)
664
-	// }
665
-}
666
-
667
-func TestPostImagesPush(t *testing.T) {
668
-	//FIXME: Use staging in order to perform tests
669
-	// runtime, err := newTestRuntime()
670
-	// if err != nil {
671
-	// 	t.Fatal(err)
672
-	// }
673
-	// defer nuke(runtime)
674
-
675
-	// srv := &Server{runtime: runtime}
676
-
677
-	// stdin, stdinPipe := io.Pipe()
678
-	// stdout, stdoutPipe := io.Pipe()
679
-
680
-	// c1 := make(chan struct{})
681
-	// go func() {
682
-	// 	r := &hijackTester{
683
-	// 		ResponseRecorder: httptest.NewRecorder(),
684
-	// 		in:               stdin,
685
-	// 		out:              stdoutPipe,
686
-	// 	}
687
-
688
-	// 	req, err := http.NewRequest("POST", "/images/docker-ut/push", bytes.NewReader([]byte{}))
689
-	// 	if err != nil {
690
-	// 		t.Fatal(err)
691
-	// 	}
692
-
693
-	// 	body, err := postImagesPush(srv, r, req, map[string]string{"name": "docker-ut"})
694
-	// 	close(c1)
695
-	// 	if err != nil {
696
-	// 		t.Fatal(err)
697
-	// 	}
698
-	// 	if body != nil {
699
-	// 		t.Fatalf("No body expected, received: %s\n", body)
700
-	// 	}
701
-	// }()
702
-
703
-	// // Acknowledge hijack
704
-	// setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() {
705
-	// 	stdout.Read([]byte{})
706
-	// 	stdout.Read(make([]byte, 4096))
707
-	// })
708
-
709
-	// setTimeout(t, "Waiting for imagesCreate output", 5*time.Second, func() {
710
-	// 	reader := bufio.NewReader(stdout)
711
-	// 	line, err := reader.ReadString('\n')
712
-	// 	if err != nil {
713
-	// 		t.Fatal(err)
714
-	// 	}
715
-	// 	if !strings.HasPrefix(line, "Processing checksum") {
716
-	// 		t.Fatalf("Processing checksum..., found %s", line)
717
-	// 	}
718
-	// })
719
-
720
-	// // Close pipes (client disconnects)
721
-	// if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
722
-	// 	t.Fatal(err)
723
-	// }
724
-
725
-	// // Wait for imagesPush to finish, the client disconnected, therefore, Push finished his job
726
-	// setTimeout(t, "Waiting for imagesPush timed out", 10*time.Second, func() {
727
-	// 	<-c1
728
-	// })
729
-}
730
-
731
-func TestPostImagesTag(t *testing.T) {
732
-	// FIXME: Use staging in order to perform tests
733
-
734
-	// runtime, err := newTestRuntime()
735
-	// if err != nil {
736
-	// 	t.Fatal(err)
737
-	// }
738
-	// defer nuke(runtime)
739
-
740
-	// srv := &Server{runtime: runtime}
741
-
742
-	// r := httptest.NewRecorder()
743
-
744
-	// req, err := http.NewRequest("POST", "/images/docker-ut/tag?repo=testrepo&tag=testtag", bytes.NewReader([]byte{}))
745
-	// if err != nil {
746
-	// 	t.Fatal(err)
747
-	// }
748
-
749
-	// body, err := postImagesTag(srv, r, req, map[string]string{"name": "docker-ut"})
750
-	// if err != nil {
751
-	// 	t.Fatal(err)
752
-	// }
753
-
754
-	// if body != nil {
755
-	// 	t.Fatalf("No body expected, received: %s\n", body)
756
-	// }
757
-	// if r.Code != http.StatusCreated {
758
-	// 	t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
759
-	// }
760
-}
761
-
762 524
 func TestPostContainersCreate(t *testing.T) {
763
-	runtime, err := newTestRuntime()
764
-	if err != nil {
765
-		t.Fatal(err)
766
-	}
525
+	runtime := mkRuntime(t)
767 526
 	defer nuke(runtime)
768 527
 
769 528
 	srv := &Server{runtime: runtime}
... ...
@@ -814,10 +596,7 @@ func TestPostContainersCreate(t *testing.T) {
814 814
 }
815 815
 
816 816
 func TestPostContainersKill(t *testing.T) {
817
-	runtime, err := newTestRuntime()
818
-	if err != nil {
819
-		t.Fatal(err)
820
-	}
817
+	runtime := mkRuntime(t)
821 818
 	defer nuke(runtime)
822 819
 
823 820
 	srv := &Server{runtime: runtime}
... ...
@@ -859,10 +638,7 @@ func TestPostContainersKill(t *testing.T) {
859 859
 }
860 860
 
861 861
 func TestPostContainersRestart(t *testing.T) {
862
-	runtime, err := newTestRuntime()
863
-	if err != nil {
864
-		t.Fatal(err)
865
-	}
862
+	runtime := mkRuntime(t)
866 863
 	defer nuke(runtime)
867 864
 
868 865
 	srv := &Server{runtime: runtime}
... ...
@@ -916,10 +692,7 @@ func TestPostContainersRestart(t *testing.T) {
916 916
 }
917 917
 
918 918
 func TestPostContainersStart(t *testing.T) {
919
-	runtime, err := newTestRuntime()
920
-	if err != nil {
921
-		t.Fatal(err)
922
-	}
919
+	runtime := mkRuntime(t)
923 920
 	defer nuke(runtime)
924 921
 
925 922
 	srv := &Server{runtime: runtime}
... ...
@@ -969,10 +742,7 @@ func TestPostContainersStart(t *testing.T) {
969 969
 }
970 970
 
971 971
 func TestPostContainersStop(t *testing.T) {
972
-	runtime, err := newTestRuntime()
973
-	if err != nil {
974
-		t.Fatal(err)
975
-	}
972
+	runtime := mkRuntime(t)
976 973
 	defer nuke(runtime)
977 974
 
978 975
 	srv := &Server{runtime: runtime}
... ...
@@ -1019,10 +789,7 @@ func TestPostContainersStop(t *testing.T) {
1019 1019
 }
1020 1020
 
1021 1021
 func TestPostContainersWait(t *testing.T) {
1022
-	runtime, err := newTestRuntime()
1023
-	if err != nil {
1024
-		t.Fatal(err)
1025
-	}
1022
+	runtime := mkRuntime(t)
1026 1023
 	defer nuke(runtime)
1027 1024
 
1028 1025
 	srv := &Server{runtime: runtime}
... ...
@@ -1064,10 +831,7 @@ func TestPostContainersWait(t *testing.T) {
1064 1064
 }
1065 1065
 
1066 1066
 func TestPostContainersAttach(t *testing.T) {
1067
-	runtime, err := newTestRuntime()
1068
-	if err != nil {
1069
-		t.Fatal(err)
1070
-	}
1067
+	runtime := mkRuntime(t)
1071 1068
 	defer nuke(runtime)
1072 1069
 
1073 1070
 	srv := &Server{runtime: runtime}
... ...
@@ -1153,10 +917,7 @@ func TestPostContainersAttach(t *testing.T) {
1153 1153
 // FIXME: Test deleting container with volume
1154 1154
 // FIXME: Test deleting volume in use by other container
1155 1155
 func TestDeleteContainers(t *testing.T) {
1156
-	runtime, err := newTestRuntime()
1157
-	if err != nil {
1158
-		t.Fatal(err)
1159
-	}
1156
+	runtime := mkRuntime(t)
1160 1157
 	defer nuke(runtime)
1161 1158
 
1162 1159
 	srv := &Server{runtime: runtime}
... ...
@@ -1196,10 +957,7 @@ func TestDeleteContainers(t *testing.T) {
1196 1196
 }
1197 1197
 
1198 1198
 func TestOptionsRoute(t *testing.T) {
1199
-	runtime, err := newTestRuntime()
1200
-	if err != nil {
1201
-		t.Fatal(err)
1202
-	}
1199
+	runtime := mkRuntime(t)
1203 1200
 	defer nuke(runtime)
1204 1201
 
1205 1202
 	srv := &Server{runtime: runtime, enableCors: true}
... ...
@@ -1222,10 +980,7 @@ func TestOptionsRoute(t *testing.T) {
1222 1222
 }
1223 1223
 
1224 1224
 func TestGetEnabledCors(t *testing.T) {
1225
-	runtime, err := newTestRuntime()
1226
-	if err != nil {
1227
-		t.Fatal(err)
1228
-	}
1225
+	runtime := mkRuntime(t)
1229 1226
 	defer nuke(runtime)
1230 1227
 
1231 1228
 	srv := &Server{runtime: runtime, enableCors: true}
... ...
@@ -1263,10 +1018,7 @@ func TestGetEnabledCors(t *testing.T) {
1263 1263
 }
1264 1264
 
1265 1265
 func TestDeleteImages(t *testing.T) {
1266
-	runtime, err := newTestRuntime()
1267
-	if err != nil {
1268
-		t.Fatal(err)
1269
-	}
1266
+	runtime := mkRuntime(t)
1270 1267
 	defer nuke(runtime)
1271 1268
 
1272 1269
 	srv := &Server{runtime: runtime}
... ...
@@ -173,6 +173,27 @@ func (b *buildFile) CmdEntrypoint(args string) error {
173 173
 	return nil
174 174
 }
175 175
 
176
+func (b *buildFile) CmdVolume(args string) error {
177
+	if args == "" {
178
+		return fmt.Errorf("Volume cannot be empty")
179
+	}
180
+
181
+	var volume []string
182
+	if err := json.Unmarshal([]byte(args), &volume); err != nil {
183
+		volume = []string{args}
184
+	}
185
+	if b.config.Volumes == nil {
186
+		b.config.Volumes = NewPathOpts()
187
+	}
188
+	for _, v := range volume {
189
+		b.config.Volumes[v] = struct{}{}
190
+	}
191
+	if err := b.commit("", b.config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil {
192
+		return err
193
+	}
194
+	return nil
195
+}
196
+
176 197
 func (b *buildFile) addRemote(container *Container, orig, dest string) error {
177 198
 	file, err := utils.Download(orig, ioutil.Discard)
178 199
 	if err != nil {
... ...
@@ -90,16 +90,22 @@ CMD Hello world
90 90
 `,
91 91
 		nil,
92 92
 	},
93
+
94
+	{
95
+		`
96
+from %s
97
+VOLUME /test
98
+CMD Hello world
99
+`,
100
+		nil,
101
+	},
93 102
 }
94 103
 
95 104
 // FIXME: test building with 2 successive overlapping ADD commands
96 105
 
97 106
 func TestBuild(t *testing.T) {
98 107
 	for _, ctx := range testContexts {
99
-		runtime, err := newTestRuntime()
100
-		if err != nil {
101
-			t.Fatal(err)
102
-		}
108
+		runtime := mkRuntime(t)
103 109
 		defer nuke(runtime)
104 110
 
105 111
 		srv := &Server{
... ...
@@ -114,3 +120,39 @@ func TestBuild(t *testing.T) {
114 114
 		}
115 115
 	}
116 116
 }
117
+
118
+func TestVolume(t *testing.T) {
119
+	runtime, err := newTestRuntime()
120
+	if err != nil {
121
+		t.Fatal(err)
122
+	}
123
+	defer nuke(runtime)
124
+
125
+	srv := &Server{
126
+		runtime:     runtime,
127
+		pullingPool: make(map[string]struct{}),
128
+		pushingPool: make(map[string]struct{}),
129
+	}
130
+
131
+	buildfile := NewBuildFile(srv, ioutil.Discard)
132
+	imgId, err := buildfile.Build(mkTestContext(`
133
+from %s
134
+VOLUME /test
135
+CMD Hello world
136
+`, nil, t))
137
+	if err != nil {
138
+		t.Fatal(err)
139
+	}
140
+	img, err := srv.ImageInspect(imgId)
141
+	if err != nil {
142
+		t.Fatal(err)
143
+	}
144
+	if len(img.Config.Volumes) == 0 {
145
+		t.Fail()
146
+	}
147
+	for key, _ := range img.Config.Volumes {
148
+		if key != "/test" {
149
+			t.Fail()
150
+		}
151
+	}
152
+}
... ...
@@ -89,6 +89,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
89 89
 		{"login", "Register or Login to the docker registry server"},
90 90
 		{"logs", "Fetch the logs of a container"},
91 91
 		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
92
+		{"top", "Lookup the running processes of a container"},
92 93
 		{"ps", "List containers"},
93 94
 		{"pull", "Pull an image or a repository from the docker registry server"},
94 95
 		{"push", "Push an image or a repository to the docker registry server"},
... ...
@@ -279,15 +280,22 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
279 279
 		return readStringOnRawTerminal(stdin, stdout, false)
280 280
 	}
281 281
 
282
-	oldState, err := term.SetRawTerminal(cli.terminalFd)
282
+	cmd := Subcmd("login", "[OPTIONS]", "Register or Login to the docker registry server")
283
+	flUsername := cmd.String("u", "", "username")
284
+	flPassword := cmd.String("p", "", "password")
285
+	flEmail := cmd.String("e", "", "email")
286
+	err := cmd.Parse(args)
283 287
 	if err != nil {
284
-		return err
288
+		return nil
285 289
 	}
286
-	defer term.RestoreTerminal(cli.terminalFd, oldState)
287 290
 
288
-	cmd := Subcmd("login", "", "Register or Login to the docker registry server")
289
-	if err := cmd.Parse(args); err != nil {
290
-		return nil
291
+	var oldState *term.State
292
+	if *flUsername == "" || *flPassword == "" || *flEmail == "" {
293
+		oldState, err = term.SetRawTerminal(cli.terminalFd)
294
+		if err != nil {
295
+			return err
296
+		}
297
+		defer term.RestoreTerminal(cli.terminalFd, oldState)
291 298
 	}
292 299
 
293 300
 	var (
... ...
@@ -296,30 +304,42 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
296 296
 		email    string
297 297
 	)
298 298
 
299
-	fmt.Fprintf(cli.out, "Username (%s):", cli.authConfig.Username)
300
-	username = readAndEchoString(cli.in, cli.out)
301
-	if username == "" {
302
-		username = cli.authConfig.Username
299
+	if *flUsername == "" {
300
+		fmt.Fprintf(cli.out, "Username (%s): ", cli.authConfig.Username)
301
+		username = readAndEchoString(cli.in, cli.out)
302
+		if username == "" {
303
+			username = cli.authConfig.Username
304
+		}
305
+	} else {
306
+		username = *flUsername
303 307
 	}
304 308
 	if username != cli.authConfig.Username {
305
-		fmt.Fprintf(cli.out, "Password: ")
306
-		password = readString(cli.in, cli.out)
307
-
308
-		if password == "" {
309
-			return fmt.Errorf("Error : Password Required")
309
+		if *flPassword == "" {
310
+			fmt.Fprintf(cli.out, "Password: ")
311
+			password = readString(cli.in, cli.out)
312
+			if password == "" {
313
+				return fmt.Errorf("Error : Password Required")
314
+			}
315
+		} else {
316
+			password = *flPassword
310 317
 		}
311 318
 
312
-		fmt.Fprintf(cli.out, "Email (%s): ", cli.authConfig.Email)
313
-		email = readAndEchoString(cli.in, cli.out)
314
-		if email == "" {
315
-			email = cli.authConfig.Email
319
+		if *flEmail == "" {
320
+			fmt.Fprintf(cli.out, "Email (%s): ", cli.authConfig.Email)
321
+			email = readAndEchoString(cli.in, cli.out)
322
+			if email == "" {
323
+				email = cli.authConfig.Email
324
+			}
325
+		} else {
326
+			email = *flEmail
316 327
 		}
317 328
 	} else {
318 329
 		password = cli.authConfig.Password
319 330
 		email = cli.authConfig.Email
320 331
 	}
321
-	term.RestoreTerminal(cli.terminalFd, oldState)
322
-
332
+	if oldState != nil {
333
+		term.RestoreTerminal(cli.terminalFd, oldState)
334
+	}
323 335
 	cli.authConfig.Username = username
324 336
 	cli.authConfig.Password = password
325 337
 	cli.authConfig.Email = email
... ...
@@ -554,6 +574,33 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
554 554
 	return nil
555 555
 }
556 556
 
557
+func (cli *DockerCli) CmdTop(args ...string) error {
558
+	cmd := Subcmd("top", "CONTAINER", "Lookup the running processes of a container")
559
+	if err := cmd.Parse(args); err != nil {
560
+		return nil
561
+	}
562
+	if cmd.NArg() != 1 {
563
+		cmd.Usage()
564
+		return nil
565
+	}
566
+	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top", nil)
567
+	if err != nil {
568
+		return err
569
+	}
570
+	var procs []APITop
571
+	err = json.Unmarshal(body, &procs)
572
+	if err != nil {
573
+		return err
574
+	}
575
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
576
+	fmt.Fprintln(w, "PID\tTTY\tTIME\tCMD")
577
+	for _, proc := range procs {
578
+		fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", proc.PID, proc.Tty, proc.Time, proc.Cmd)
579
+	}
580
+	w.Flush()
581
+	return nil
582
+}
583
+
557 584
 func (cli *DockerCli) CmdPort(args ...string) error {
558 585
 	cmd := Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
559 586
 	if err := cmd.Parse(args); err != nil {
... ...
@@ -564,6 +611,13 @@ func (cli *DockerCli) CmdPort(args ...string) error {
564 564
 		return nil
565 565
 	}
566 566
 
567
+	port := cmd.Arg(1)
568
+	proto := "Tcp"
569
+	parts := strings.SplitN(port, "/", 2)
570
+	if len(parts) == 2 && len(parts[1]) != 0 {
571
+		port = parts[0]
572
+		proto = strings.ToUpper(parts[1][:1]) + strings.ToLower(parts[1][1:])
573
+	}
567 574
 	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
568 575
 	if err != nil {
569 576
 		return err
... ...
@@ -574,7 +628,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
574 574
 		return err
575 575
 	}
576 576
 
577
-	if frontend, exists := out.NetworkSettings.PortMapping[cmd.Arg(1)]; exists {
577
+	if frontend, exists := out.NetworkSettings.PortMapping[proto][port]; exists {
578 578
 		fmt.Fprintf(cli.out, "%s\n", frontend)
579 579
 	} else {
580 580
 		return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0))
... ...
@@ -767,7 +821,9 @@ func (cli *DockerCli) CmdPull(args ...string) error {
767 767
 	}
768 768
 
769 769
 	remote, parsedTag := utils.ParseRepositoryTag(cmd.Arg(0))
770
-	*tag = parsedTag
770
+	if *tag == "" {
771
+		*tag = parsedTag
772
+	}
771 773
 
772 774
 	v := url.Values{}
773 775
 	v.Set("fromImage", remote)
... ...
@@ -59,79 +59,6 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error
59 59
 	return nil
60 60
 }
61 61
 
62
-/*TODO
63
-func cmdImages(srv *Server, args ...string) (string, error) {
64
-	stdout, stdoutPipe := io.Pipe()
65
-
66
-	go func() {
67
-		if err := srv.CmdImages(nil, stdoutPipe, args...); err != nil {
68
-			return
69
-		}
70
-
71
-		// force the pipe closed, so that the code below gets an EOF
72
-		stdoutPipe.Close()
73
-	}()
74
-
75
-	output, err := ioutil.ReadAll(stdout)
76
-	if err != nil {
77
-		return "", err
78
-	}
79
-
80
-	// Cleanup pipes
81
-	return string(output), closeWrap(stdout, stdoutPipe)
82
-}
83
-
84
-// TestImages checks that 'docker images' displays information correctly
85
-func TestImages(t *testing.T) {
86
-
87
-	runtime, err := newTestRuntime()
88
-	if err != nil {
89
-		t.Fatal(err)
90
-	}
91
-	defer nuke(runtime)
92
-
93
-	srv := &Server{runtime: runtime}
94
-
95
-	output, err := cmdImages(srv)
96
-
97
-	if !strings.Contains(output, "REPOSITORY") {
98
-		t.Fatal("'images' should have a header")
99
-	}
100
-	if !strings.Contains(output, "docker-ut") {
101
-		t.Fatal("'images' should show the docker-ut image")
102
-	}
103
-	if !strings.Contains(output, "e9aa60c60128") {
104
-		t.Fatal("'images' should show the docker-ut image id")
105
-	}
106
-
107
-	output, err = cmdImages(srv, "-q")
108
-
109
-	if strings.Contains(output, "REPOSITORY") {
110
-		t.Fatal("'images -q' should not have a header")
111
-	}
112
-	if strings.Contains(output, "docker-ut") {
113
-		t.Fatal("'images' should not show the docker-ut image name")
114
-	}
115
-	if !strings.Contains(output, "e9aa60c60128") {
116
-		t.Fatal("'images' should show the docker-ut image id")
117
-	}
118
-
119
-	output, err = cmdImages(srv, "-viz")
120
-
121
-	if !strings.HasPrefix(output, "digraph docker {") {
122
-		t.Fatal("'images -v' should start with the dot header")
123
-	}
124
-	if !strings.HasSuffix(output, "}\n") {
125
-		t.Fatal("'images -v' should end with a '}'")
126
-	}
127
-	if !strings.Contains(output, "base -> \"e9aa60c60128\" [style=invis]") {
128
-		t.Fatal("'images -v' should have the docker-ut image id node")
129
-	}
130
-
131
-	// todo: add checks for -a
132
-}
133
-
134
-*/
135 62
 
136 63
 // TestRunHostname checks that 'docker run -h' correctly sets a custom hostname
137 64
 func TestRunHostname(t *testing.T) {
... ...
@@ -164,163 +91,6 @@ func TestRunHostname(t *testing.T) {
164 164
 
165 165
 }
166 166
 
167
-/*
168
-func TestRunExit(t *testing.T) {
169
-	runtime, err := newTestRuntime()
170
-	if err != nil {
171
-		t.Fatal(err)
172
-	}
173
-	defer nuke(runtime)
174
-
175
-	srv := &Server{runtime: runtime}
176
-
177
-	stdin, stdinPipe := io.Pipe()
178
-	stdout, stdoutPipe := io.Pipe()
179
-	c1 := make(chan struct{})
180
-	go func() {
181
-		srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", GetTestImage(runtime).Id, "/bin/cat")
182
-		close(c1)
183
-	}()
184
-
185
-	setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
186
-		if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
187
-			t.Fatal(err)
188
-		}
189
-	})
190
-
191
-	container := runtime.List()[0]
192
-
193
-	// Closing /bin/cat stdin, expect it to exit
194
-	p, err := container.StdinPipe()
195
-	if err != nil {
196
-		t.Fatal(err)
197
-	}
198
-	if err := p.Close(); err != nil {
199
-		t.Fatal(err)
200
-	}
201
-
202
-	// as the process exited, CmdRun must finish and unblock. Wait for it
203
-	setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() {
204
-		<-c1
205
-		cmdWait(srv, container)
206
-	})
207
-
208
-	// Make sure that the client has been disconnected
209
-	setTimeout(t, "The client should have been disconnected once the remote process exited.", 2*time.Second, func() {
210
-		// Expecting pipe i/o error, just check that read does not block
211
-		stdin.Read([]byte{})
212
-	})
213
-
214
-	// Cleanup pipes
215
-	if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
216
-		t.Fatal(err)
217
-	}
218
-}
219
-
220
-// Expected behaviour: the process dies when the client disconnects
221
-func TestRunDisconnect(t *testing.T) {
222
-	runtime, err := newTestRuntime()
223
-	if err != nil {
224
-		t.Fatal(err)
225
-	}
226
-	defer nuke(runtime)
227
-
228
-	srv := &Server{runtime: runtime}
229
-
230
-	stdin, stdinPipe := io.Pipe()
231
-	stdout, stdoutPipe := io.Pipe()
232
-	c1 := make(chan struct{})
233
-	go func() {
234
-		// We're simulating a disconnect so the return value doesn't matter. What matters is the
235
-		// fact that CmdRun returns.
236
-		srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", GetTestImage(runtime).Id, "/bin/cat")
237
-		close(c1)
238
-	}()
239
-
240
-	setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
241
-		if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
242
-			t.Fatal(err)
243
-		}
244
-	})
245
-
246
-	// Close pipes (simulate disconnect)
247
-	if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
248
-		t.Fatal(err)
249
-	}
250
-
251
-	// as the pipes are close, we expect the process to die,
252
-	// therefore CmdRun to unblock. Wait for CmdRun
253
-	setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() {
254
-		<-c1
255
-	})
256
-
257
-	// Client disconnect after run -i should cause stdin to be closed, which should
258
-	// cause /bin/cat to exit.
259
-	setTimeout(t, "Waiting for /bin/cat to exit timed out", 2*time.Second, func() {
260
-		container := runtime.List()[0]
261
-		container.Wait()
262
-		if container.State.Running {
263
-			t.Fatalf("/bin/cat is still running after closing stdin")
264
-		}
265
-	})
266
-}
267
-
268
-// Expected behaviour: the process dies when the client disconnects
269
-func TestRunDisconnectTty(t *testing.T) {
270
-	runtime, err := newTestRuntime()
271
-	if err != nil {
272
-		t.Fatal(err)
273
-	}
274
-	defer nuke(runtime)
275
-
276
-	srv := &Server{runtime: runtime}
277
-
278
-	stdin, stdinPipe := io.Pipe()
279
-	stdout, stdoutPipe := io.Pipe()
280
-	c1 := make(chan struct{})
281
-	go func() {
282
-		// We're simulating a disconnect so the return value doesn't matter. What matters is the
283
-		// fact that CmdRun returns.
284
-		srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", "-t", GetTestImage(runtime).Id, "/bin/cat")
285
-		close(c1)
286
-	}()
287
-
288
-	setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() {
289
-		for {
290
-			// Client disconnect after run -i should keep stdin out in TTY mode
291
-			l := runtime.List()
292
-			if len(l) == 1 && l[0].State.Running {
293
-				break
294
-			}
295
-
296
-			time.Sleep(10 * time.Millisecond)
297
-		}
298
-	})
299
-
300
-	// Client disconnect after run -i should keep stdin out in TTY mode
301
-	container := runtime.List()[0]
302
-
303
-	setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
304
-		if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
305
-			t.Fatal(err)
306
-		}
307
-	})
308
-
309
-	// Close pipes (simulate disconnect)
310
-	if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
311
-		t.Fatal(err)
312
-	}
313
-
314
-	// In tty mode, we expect the process to stay alive even after client's stdin closes.
315
-	// Do not wait for run to finish
316
-
317
-	// Give some time to monitor to do his thing
318
-	container.WaitTimeout(500 * time.Millisecond)
319
-	if !container.State.Running {
320
-		t.Fatalf("/bin/cat should  still be running after closing stdin (tty mode)")
321
-	}
322
-}
323
-*/
324 167
 
325 168
 // TestAttachStdin checks attaching to stdin without stdout and stderr.
326 169
 // 'docker run -i -a stdin' should sends the client's stdin to the command,
... ...
@@ -387,74 +157,3 @@ func TestRunAttachStdin(t *testing.T) {
387 387
 		}
388 388
 	}
389 389
 }
390
-
391
-/*
392
-// Expected behaviour, the process stays alive when the client disconnects
393
-func TestAttachDisconnect(t *testing.T) {
394
-	runtime, err := newTestRuntime()
395
-	if err != nil {
396
-		t.Fatal(err)
397
-	}
398
-	defer nuke(runtime)
399
-
400
-	srv := &Server{runtime: runtime}
401
-
402
-	container, err := NewBuilder(runtime).Create(
403
-		&Config{
404
-			Image:     GetTestImage(runtime).Id,
405
-			CpuShares: 1000,
406
-			Memory:    33554432,
407
-			Cmd:       []string{"/bin/cat"},
408
-			OpenStdin: true,
409
-		},
410
-	)
411
-	if err != nil {
412
-		t.Fatal(err)
413
-	}
414
-	defer runtime.Destroy(container)
415
-
416
-	// Start the process
417
-	if err := container.Start(); err != nil {
418
-		t.Fatal(err)
419
-	}
420
-
421
-	stdin, stdinPipe := io.Pipe()
422
-	stdout, stdoutPipe := io.Pipe()
423
-
424
-	// Attach to it
425
-	c1 := make(chan struct{})
426
-	go func() {
427
-		// We're simulating a disconnect so the return value doesn't matter. What matters is the
428
-		// fact that CmdAttach returns.
429
-		srv.CmdAttach(stdin, rcli.NewDockerLocalConn(stdoutPipe), container.Id)
430
-		close(c1)
431
-	}()
432
-
433
-	setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
434
-		if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
435
-			t.Fatal(err)
436
-		}
437
-	})
438
-	// Close pipes (client disconnects)
439
-	if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
440
-		t.Fatal(err)
441
-	}
442
-
443
-	// Wait for attach to finish, the client disconnected, therefore, Attach finished his job
444
-	setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() {
445
-		<-c1
446
-	})
447
-
448
-	// We closed stdin, expect /bin/cat to still be running
449
-	// Wait a little bit to make sure container.monitor() did his thing
450
-	err = container.WaitTimeout(500 * time.Millisecond)
451
-	if err == nil || !container.State.Running {
452
-		t.Fatalf("/bin/cat is not running after closing stdin")
453
-	}
454
-
455
-	// Try to avoid the timeoout in destroy. Best effort, don't check error
456
-	cStdin, _ := container.StdinPipe()
457
-	cStdin.Close()
458
-	container.Wait()
459
-}
460
-*/
... ...
@@ -270,6 +270,26 @@ func (container *Container) ToDisk() (err error) {
270 270
 	return ioutil.WriteFile(container.jsonPath(), data, 0666)
271 271
 }
272 272
 
273
+func (container *Container) ReadHostConfig() (*HostConfig, error) {
274
+	data, err := ioutil.ReadFile(container.hostConfigPath())
275
+	if err != nil {
276
+		return &HostConfig{}, err
277
+	}
278
+	hostConfig := &HostConfig{}
279
+	if err := json.Unmarshal(data, hostConfig); err != nil {
280
+		return &HostConfig{}, err
281
+	}
282
+	return hostConfig, nil
283
+}
284
+
285
+func (container *Container) SaveHostConfig(hostConfig *HostConfig) (err error) {
286
+	data, err := json.Marshal(hostConfig)
287
+	if err != nil {
288
+		return
289
+	}
290
+	return ioutil.WriteFile(container.hostConfigPath(), data, 0666)
291
+}
292
+
273 293
 func (container *Container) generateLXCConfig() error {
274 294
 	fo, err := os.Create(container.lxcConfigPath())
275 295
 	if err != nil {
... ...
@@ -473,6 +493,9 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
473 473
 func (container *Container) Start(hostConfig *HostConfig) error {
474 474
 	container.State.Lock()
475 475
 	defer container.State.Unlock()
476
+	if len(hostConfig.Binds) == 0 {
477
+		hostConfig, _ = container.ReadHostConfig()
478
+	}
476 479
 
477 480
 	if container.State.Running {
478 481
 		return fmt.Errorf("The container %s is already running.", container.ID)
... ...
@@ -649,6 +672,7 @@ func (container *Container) Start(hostConfig *HostConfig) error {
649 649
 	container.waitLock = make(chan struct{})
650 650
 
651 651
 	container.ToDisk()
652
+	container.SaveHostConfig(hostConfig)
652 653
 	go container.monitor()
653 654
 	return nil
654 655
 }
... ...
@@ -987,6 +1011,10 @@ func (container *Container) ReadLog(name string) (io.Reader, error) {
987 987
 	return os.Open(container.logPath(name))
988 988
 }
989 989
 
990
+func (container *Container) hostConfigPath() string {
991
+	return path.Join(container.root, "hostconfig.json")
992
+}
993
+
990 994
 func (container *Container) jsonPath() string {
991 995
 	return path.Join(container.root, "config.json")
992 996
 }
... ...
@@ -1046,10 +1046,7 @@ func TestEnv(t *testing.T) {
1046 1046
 }
1047 1047
 
1048 1048
 func TestEntrypoint(t *testing.T) {
1049
-	runtime, err := newTestRuntime()
1050
-	if err != nil {
1051
-		t.Fatal(err)
1052
-	}
1049
+	runtime := mkRuntime(t)
1053 1050
 	defer nuke(runtime)
1054 1051
 	container, err := NewBuilder(runtime).Create(
1055 1052
 		&Config{
... ...
@@ -1125,10 +1122,7 @@ func TestLXCConfig(t *testing.T) {
1125 1125
 }
1126 1126
 
1127 1127
 func BenchmarkRunSequencial(b *testing.B) {
1128
-	runtime, err := newTestRuntime()
1129
-	if err != nil {
1130
-		b.Fatal(err)
1131
-	}
1128
+	runtime := mkRuntime(b)
1132 1129
 	defer nuke(runtime)
1133 1130
 	for i := 0; i < b.N; i++ {
1134 1131
 		container, err := NewBuilder(runtime).Create(&Config{
... ...
@@ -1154,10 +1148,7 @@ func BenchmarkRunSequencial(b *testing.B) {
1154 1154
 }
1155 1155
 
1156 1156
 func BenchmarkRunParallel(b *testing.B) {
1157
-	runtime, err := newTestRuntime()
1158
-	if err != nil {
1159
-		b.Fatal(err)
1160
-	}
1157
+	runtime := mkRuntime(b)
1161 1158
 	defer nuke(runtime)
1162 1159
 
1163 1160
 	var tasks []chan error
... ...
@@ -29,6 +29,11 @@ You can still call an old version of the api using /v1.0/images/<name>/insert
29 29
 What's new
30 30
 ----------
31 31
 
32
+Listing processes (/top):
33
+
34
+- List the processes inside a container
35
+
36
+
32 37
 Builder (/build):
33 38
 
34 39
 - Simplify the upload of the build context
... ...
@@ -220,6 +220,46 @@ Inspect a container
220 220
 	:statuscode 500: server error
221 221
 
222 222
 
223
+List processes running inside a container
224
+*****************************************
225
+
226
+.. http:get:: /containers/(id)/top
227
+
228
+	List processes running inside the container ``id``
229
+
230
+	**Example request**:
231
+
232
+	.. sourcecode:: http
233
+
234
+	   GET /containers/4fa6e0f0c678/top HTTP/1.1
235
+
236
+	**Example response**:
237
+
238
+	.. sourcecode:: http
239
+
240
+	   HTTP/1.1 200 OK
241
+	   Content-Type: application/json
242
+
243
+	   [
244
+		{
245
+		 "PID":"11935",
246
+		 "Tty":"pts/2",
247
+		 "Time":"00:00:00",
248
+		 "Cmd":"sh"
249
+		},
250
+		{
251
+		 "PID":"12140",
252
+		 "Tty":"pts/2",
253
+		 "Time":"00:00:00",
254
+		 "Cmd":"sleep"
255
+		}
256
+	   ]
257
+
258
+	:statuscode 200: no error
259
+	:statuscode 404: no such container
260
+	:statuscode 500: server error
261
+
262
+
223 263
 Inspect changes on a container's filesystem
224 264
 *******************************************
225 265
 
... ...
@@ -52,5 +52,6 @@ Available Commands
52 52
    command/start
53 53
    command/stop
54 54
    command/tag
55
+   command/top
55 56
    command/version
56 57
    command/wait
... ...
@@ -8,6 +8,10 @@
8 8
 
9 9
 ::
10 10
 
11
-    Usage: docker login
11
+    Usage: docker login [OPTIONS]
12 12
 
13 13
     Register or Login to the docker registry server
14
+
15
+    -e="": email
16
+    -p="": password
17
+    -u="": username
14 18
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+:title: Top Command
1
+:description: Lookup the running processes of a container
2
+:keywords: top, docker, container, documentation
3
+
4
+=======================================================
5
+``top`` -- Lookup the running processes of a container
6
+=======================================================
7
+
8
+::
9
+
10
+    Usage: docker top CONTAINER
11
+
12
+    Lookup the running processes of a container
... ...
@@ -48,12 +48,12 @@ Docker will ignore lines in Dockerfiles prefixed with "`#`", so you may add
48 48
 comment lines. A comment marker in the rest of the line will be treated as an
49 49
 argument.
50 50
 
51
-2. Instructions
51
+3. Instructions
52 52
 ===============
53 53
 
54 54
 Docker builder comes with a set of instructions, described below.
55 55
 
56
-2.1 FROM
56
+3.1 FROM
57 57
 --------
58 58
 
59 59
     ``FROM <image>``
... ...
@@ -65,7 +65,7 @@ a valid Dockerfile must have it as its first instruction.
65 65
 create multiple images. Simply make a note of the last image id output by the 
66 66
 commit before each new `FROM` command.
67 67
 
68
-2.2 MAINTAINER
68
+3.2 MAINTAINER
69 69
 --------------
70 70
 
71 71
     ``MAINTAINER <name>``
... ...
@@ -73,7 +73,7 @@ commit before each new `FROM` command.
73 73
 The `MAINTAINER` instruction allows you to set the Author field of the generated 
74 74
 images.
75 75
 
76
-2.3 RUN
76
+3.3 RUN
77 77
 -------
78 78
 
79 79
     ``RUN <command>``
... ...
@@ -86,7 +86,7 @@ Layering `RUN` instructions and generating commits conforms to the
86 86
 core concepts of Docker where commits are cheap and containers can be created
87 87
 from any point in an image's history, much like source control.
88 88
 
89
-2.4 CMD
89
+3.4 CMD
90 90
 -------
91 91
 
92 92
     ``CMD <command>``
... ...
@@ -100,7 +100,7 @@ This is functionally equivalent to running
100 100
     the result; `CMD` does not execute anything at build time, but specifies the
101 101
     intended command for the image.
102 102
 
103
-2.5 EXPOSE
103
+3.5 EXPOSE
104 104
 ----------
105 105
 
106 106
     ``EXPOSE <port> [<port>...]``
... ...
@@ -109,7 +109,7 @@ The `EXPOSE` instruction sets ports to be publicly exposed when running the
109 109
 image. This is functionally equivalent to running 
110 110
 `docker commit -run '{"PortSpecs": ["<port>", "<port2>"]}'` outside the builder.
111 111
 
112
-2.6 ENV
112
+3.6 ENV
113 113
 -------
114 114
 
115 115
     ``ENV <key> <value>``
... ...
@@ -121,7 +121,7 @@ functionally equivalent to prefixing the command with `<key>=<value>`
121 121
 .. note::
122 122
     The environment variables will persist when a container is run from the resulting image.
123 123
 
124
-2.7 ADD
124
+3.7 ADD
125 125
 -------
126 126
 
127 127
     ``ADD <src> <dest>``
... ...
@@ -153,14 +153,21 @@ of `<src>` will be written at `<dst>`.
153 153
 If `<dest>` doesn't exist, it is created along with all missing directories in its path. All new
154 154
 files and directories are created with mode 0700, uid and gid 0.
155 155
 
156
-2.8 ENTRYPOINT
156
+3.8 ENTRYPOINT
157 157
 -------------
158 158
 
159 159
     ``ENTRYPOINT /bin/echo``
160 160
 
161 161
 The `ENTRYPOINT` instruction adds an entry command that will not be overwritten when arguments are passed to docker run, unlike the behavior of `CMD`.  This allows arguments to be passed to the entrypoint.  i.e. `docker run <image> -d` will pass the "-d" argument to the entrypoint.
162 162
 
163
-3. Dockerfile Examples
163
+3.9 VOLUME
164
+----------
165
+
166
+    ``VOLUME ["/data"]``
167
+
168
+The `VOLUME` instruction will add one or more new volumes to any container created from the image.
169
+
170
+4. Dockerfile Examples
164 171
 ======================
165 172
 
166 173
 .. code-block:: bash
... ...
@@ -13,8 +13,8 @@ PKG_NAME=lxc-docker
13 13
 ROOT_PATH=$(shell git rev-parse --show-toplevel)
14 14
 GITHUB_PATH=github.com/dotcloud/docker
15 15
 BUILD_SRC=build_src
16
-VERSION_TAG?=v$(shell sed -E 's/.+\((.+)-.+\).+/\1/;q' changelog)
17
-VERSION=$(shell echo ${VERSION_TAG} | cut -c2-)
16
+VERSION=$(shell sed -En '0,/^\#\# /{s/^\#\# ([^ ]+).+/\1/p}' ../../CHANGELOG.md)
17
+VERSION_TAG?=v${VERSION}
18 18
 DOCKER_VERSION=${PKG_NAME}_${VERSION}
19 19
 
20 20
 all:
... ...
@@ -28,7 +28,6 @@ install:
28 28
 	mkdir -p $(DESTDIR)/usr/share/doc/lxc-docker
29 29
 	install -m 0755 src/${GITHUB_PATH}/docker/docker $(DESTDIR)/usr/bin/lxc-docker
30 30
 	cp debian/lxc-docker.1 $(DESTDIR)/usr/share/man/man1
31
-	cp debian/CHANGELOG.md $(DESTDIR)/usr/share/doc/lxc-docker/changelog
32 31
 
33 32
 debian:
34 33
 	# Prepare docker source from revision ${VERSION_TAG}
... ...
@@ -41,6 +40,7 @@ debian:
41 41
 	cp -r `ls | grep -v ${BUILD_SRC}` ${BUILD_SRC}/debian
42 42
 	cp ${ROOT_PATH}/README.md ${BUILD_SRC}
43 43
 	cp ${ROOT_PATH}/CHANGELOG.md ${BUILD_SRC}/debian
44
+	./parse_changelog.py < ../../CHANGELOG.md  > ${BUILD_SRC}/debian/changelog
44 45
 	# Cleanup
45 46
 	rm -rf `find . -name '.git*'`
46 47
 	rm -f ${DOCKER_VERSION}*
... ...
@@ -13,6 +13,9 @@ Vagrant::Config.run do |config|
13 13
 
14 14
   # Install debian packaging dependencies and create debian packages
15 15
   pkg_cmd = "apt-get -qq update; DEBIAN_FRONTEND=noninteractive apt-get install -qq -y #{PKG_DEP}; " \
16
+      "curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.1.linux-amd64.tar.gz; " \
17
+      "tar -C /usr/local -xzf /go.tar.gz; rm /usr/bin/go; " \
18
+      "ln -s /usr/local/go/bin/go /usr/bin; "\
16 19
       "export GPG_KEY='#{ENV['GPG_KEY']}'; cd /data/docker/packaging/debian; make debian"
17 20
   config.vm.provision :shell, :inline => pkg_cmd
18 21
 end
19 22
new file mode 100755
... ...
@@ -0,0 +1,23 @@
0
+#!/usr/bin/env python
1
+
2
+'Parse main CHANGELOG.md from stdin outputing on stdout the debian changelog'
3
+
4
+import sys,re, datetime
5
+
6
+on_block=False
7
+for line in sys.stdin.readlines():
8
+    line = line.strip()
9
+    if line.startswith('# ') or len(line) == 0:
10
+        continue
11
+    if line.startswith('## '):
12
+        if on_block:
13
+            print '\n -- dotCloud <ops@dotcloud.com>  {0}\n'.format(date)
14
+        version, date = line[3:].split()
15
+        date = datetime.datetime.strptime(date, '(%Y-%m-%d)').strftime(
16
+            '%a, %d %b %Y 00:00:00 -0700')
17
+        on_block = True
18
+        print 'lxc-docker ({0}-1) precise; urgency=low'.format(version)
19
+        continue
20
+    if on_block:
21
+        print '  ' + line
22
+print '\n -- dotCloud <ops@dotcloud.com>  {0}'.format(date)
... ...
@@ -141,7 +141,6 @@ func (runtime *Runtime) Register(container *Container) error {
141 141
 				utils.Debugf("Restarting")
142 142
 				container.State.Ghost = false
143 143
 				container.State.setStopped(0)
144
-				// assume empty host config
145 144
 				hostConfig := &HostConfig{}
146 145
 				if err := container.Start(hostConfig); err != nil {
147 146
 					return err
... ...
@@ -114,26 +114,6 @@ func init() {
114 114
 
115 115
 // FIXME: test that ImagePull(json=true) send correct json output
116 116
 
117
-func newTestRuntime() (*Runtime, error) {
118
-	root, err := ioutil.TempDir("", "docker-test")
119
-	if err != nil {
120
-		return nil, err
121
-	}
122
-	if err := os.Remove(root); err != nil {
123
-		return nil, err
124
-	}
125
-	if err := utils.CopyDirectory(unitTestStoreBase, root); err != nil {
126
-		return nil, err
127
-	}
128
-
129
-	runtime, err := NewRuntimeFromDirectory(root, false)
130
-	if err != nil {
131
-		return nil, err
132
-	}
133
-	runtime.UpdateCapabilities(true)
134
-	return runtime, nil
135
-}
136
-
137 117
 func GetTestImage(runtime *Runtime) *Image {
138 118
 	imgs, err := runtime.graph.All()
139 119
 	if err != nil {
... ...
@@ -148,10 +128,7 @@ func GetTestImage(runtime *Runtime) *Image {
148 148
 }
149 149
 
150 150
 func TestRuntimeCreate(t *testing.T) {
151
-	runtime, err := newTestRuntime()
152
-	if err != nil {
153
-		t.Fatal(err)
154
-	}
151
+	runtime := mkRuntime(t)
155 152
 	defer nuke(runtime)
156 153
 
157 154
 	// Make sure we start we 0 containers
... ...
@@ -223,10 +200,7 @@ func TestRuntimeCreate(t *testing.T) {
223 223
 }
224 224
 
225 225
 func TestDestroy(t *testing.T) {
226
-	runtime, err := newTestRuntime()
227
-	if err != nil {
228
-		t.Fatal(err)
229
-	}
226
+	runtime := mkRuntime(t)
230 227
 	defer nuke(runtime)
231 228
 	container, err := NewBuilder(runtime).Create(&Config{
232 229
 		Image: GetTestImage(runtime).ID,
... ...
@@ -270,10 +244,7 @@ func TestDestroy(t *testing.T) {
270 270
 }
271 271
 
272 272
 func TestGet(t *testing.T) {
273
-	runtime, err := newTestRuntime()
274
-	if err != nil {
275
-		t.Fatal(err)
276
-	}
273
+	runtime := mkRuntime(t)
277 274
 	defer nuke(runtime)
278 275
 
279 276
 	builder := NewBuilder(runtime)
... ...
@@ -323,11 +294,8 @@ func TestGet(t *testing.T) {
323 323
 }
324 324
 
325 325
 func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container, string) {
326
-	runtime, err := newTestRuntime()
327
-	if err != nil {
328
-		t.Fatal(err)
329
-	}
330
-
326
+	var err error
327
+	runtime := mkRuntime(t)
331 328
 	port := 5554
332 329
 	var container *Container
333 330
 	var strPort string
... ...
@@ -1,6 +1,7 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"bufio"
4 5
 	"errors"
5 6
 	"fmt"
6 7
 	"github.com/dotcloud/docker/auth"
... ...
@@ -12,6 +13,7 @@ import (
12 12
 	"net/http"
13 13
 	"net/url"
14 14
 	"os"
15
+	"os/exec"
15 16
 	"path"
16 17
 	"runtime"
17 18
 	"strings"
... ...
@@ -98,7 +100,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
98 98
 		return "", err
99 99
 	}
100 100
 
101
-	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), path); err != nil {
101
+	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("Downloading", "%8v/%v (%v)"), sf), path); err != nil {
102 102
 		return "", err
103 103
 	}
104 104
 	// FIXME: Handle custom repo, tag comment, author
... ...
@@ -247,6 +249,40 @@ func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
247 247
 
248 248
 }
249 249
 
250
+func (srv *Server) ContainerTop(name string) ([]APITop, error) {
251
+	if container := srv.runtime.Get(name); container != nil {
252
+		output, err := exec.Command("lxc-ps", "--name", container.ID).CombinedOutput()
253
+		if err != nil {
254
+			return nil, fmt.Errorf("Error trying to use lxc-ps: %s (%s)", err, output)
255
+		}
256
+		var procs []APITop
257
+		for i, line := range strings.Split(string(output), "\n") {
258
+			if i == 0 || len(line) == 0 {
259
+				continue
260
+			}
261
+			proc := APITop{}
262
+			scanner := bufio.NewScanner(strings.NewReader(line))
263
+			scanner.Split(bufio.ScanWords)
264
+			if !scanner.Scan() {
265
+				return nil, fmt.Errorf("Error trying to use lxc-ps")
266
+			}
267
+			// no scanner.Text because we skip container id
268
+			scanner.Scan()
269
+			proc.PID = scanner.Text()
270
+			scanner.Scan()
271
+			proc.Tty = scanner.Text()
272
+			scanner.Scan()
273
+			proc.Time = scanner.Text()
274
+			scanner.Scan()
275
+			proc.Cmd = scanner.Text()
276
+			procs = append(procs, proc)
277
+		}
278
+		return procs, nil
279
+
280
+	}
281
+	return nil, fmt.Errorf("No such container: %s", name)
282
+}
283
+
250 284
 func (srv *Server) ContainerChanges(name string) ([]Change, error) {
251 285
 	if container := srv.runtime.Get(name); container != nil {
252 286
 		return container.Changes()
... ...
@@ -343,7 +379,7 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoin
343 343
 				return err
344 344
 			}
345 345
 			defer layer.Close()
346
-			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, imgSize, out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), false, img); err != nil {
346
+			if err := srv.runtime.graph.Register(utils.ProgressReader(layer, imgSize, out, sf.FormatProgress("Downloading", "%8v/%v (%v)"), sf), false, img); err != nil {
347 347
 				return err
348 348
 			}
349 349
 		}
... ...
@@ -666,7 +702,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID,
666 666
 	}
667 667
 
668 668
 	// Send the layer
669
-	if err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("Pushing", "%v/%v (%v)"), sf), ep, token); err != nil {
669
+	if err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("Pushing", "%8v/%v (%v)"), sf), ep, token); err != nil {
670 670
 		return err
671 671
 	}
672 672
 	return nil
... ...
@@ -736,7 +772,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
736 736
 		if err != nil {
737 737
 			return err
738 738
 		}
739
-		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("Importing", "%v/%v (%v)"), sf)
739
+		archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("Importing", "%8v/%v (%v)"), sf)
740 740
 	}
741 741
 	img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
742 742
 	if err != nil {
... ...
@@ -5,10 +5,7 @@ import (
5 5
 )
6 6
 
7 7
 func TestContainerTagImageDelete(t *testing.T) {
8
-	runtime, err := newTestRuntime()
9
-	if err != nil {
10
-		t.Fatal(err)
11
-	}
8
+	runtime := mkRuntime(t)
12 9
 	defer nuke(runtime)
13 10
 
14 11
 	srv := &Server{runtime: runtime}
... ...
@@ -62,10 +59,7 @@ func TestContainerTagImageDelete(t *testing.T) {
62 62
 }
63 63
 
64 64
 func TestCreateRm(t *testing.T) {
65
-	runtime, err := newTestRuntime()
66
-	if err != nil {
67
-		t.Fatal(err)
68
-	}
65
+	runtime := mkRuntime(t)
69 66
 	defer nuke(runtime)
70 67
 
71 68
 	srv := &Server{runtime: runtime}
... ...
@@ -95,10 +89,7 @@ func TestCreateRm(t *testing.T) {
95 95
 }
96 96
 
97 97
 func TestCreateStartRestartStopStartKillRm(t *testing.T) {
98
-	runtime, err := newTestRuntime()
99
-	if err != nil {
100
-		t.Fatal(err)
101
-	}
98
+	runtime := mkRuntime(t)
102 99
 	defer nuke(runtime)
103 100
 
104 101
 	srv := &Server{runtime: runtime}
... ...
@@ -154,11 +145,9 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
154 154
 }
155 155
 
156 156
 func TestRunWithTooLowMemoryLimit(t *testing.T) {
157
-	runtime, err := newTestRuntime()
157
+	var err error
158
+	runtime := mkRuntime(t)
158 159
 	srv := &Server{runtime: runtime}
159
-	if err != nil {
160
-		t.Fatal(err)
161
-	}
162 160
 	defer nuke(runtime)
163 161
 	// Try to create a container with a memory limit of 1 byte less than the minimum allowed limit.
164 162
 	_, err = srv.ContainerCreate(
... ...
@@ -204,15 +204,15 @@ func (store *TagStore) GetImage(repoName, tagOrID string) (*Image, error) {
204 204
 	} else if repo == nil {
205 205
 		return nil, nil
206 206
 	}
207
-	//go through all the tags, to see if tag is in fact an ID
207
+	if revision, exists := repo[tagOrID]; exists {
208
+		return store.graph.Get(revision)
209
+	}
210
+	// If no matching tag is found, search through images for a matching image id
208 211
 	for _, revision := range repo {
209 212
 		if strings.HasPrefix(revision, tagOrID) {
210 213
 			return store.graph.Get(revision)
211 214
 		}
212 215
 	}
213
-	if revision, exists := repo[tagOrID]; exists {
214
-		return store.graph.Get(revision)
215
-	}
216 216
 	return nil, nil
217 217
 }
218 218
 
... ...
@@ -5,10 +5,7 @@ import (
5 5
 )
6 6
 
7 7
 func TestLookupImage(t *testing.T) {
8
-	runtime, err := newTestRuntime()
9
-	if err != nil {
10
-		t.Fatal(err)
11
-	}
8
+	runtime := mkRuntime(t)
12 9
 	defer nuke(runtime)
13 10
 
14 11
 	if img, err := runtime.repositories.LookupImage(unitTestImageName); err != nil {
... ...
@@ -20,7 +20,8 @@ func CompareConfig(a, b *Config) bool {
20 20
 	if len(a.Cmd) != len(b.Cmd) ||
21 21
 		len(a.Dns) != len(b.Dns) ||
22 22
 		len(a.Env) != len(b.Env) ||
23
-		len(a.PortSpecs) != len(b.PortSpecs) {
23
+		len(a.PortSpecs) != len(b.PortSpecs) ||
24
+		len(a.Entrypoint) != len(b.Entrypoint) {
24 25
 		return false
25 26
 	}
26 27
 
... ...
@@ -89,4 +90,7 @@ func MergeConfig(userConf, imageConf *Config) {
89 89
 	if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 {
90 90
 		userConf.Entrypoint = imageConf.Entrypoint
91 91
 	}
92
+	if userConf.Volumes == nil || len(userConf.Volumes) == 0 {
93
+		userConf.Volumes = imageConf.Volumes
94
+	}
92 95
 }
... ...
@@ -87,7 +87,7 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
87 87
 	}
88 88
 	if r.readProgress-r.lastUpdate > updateEvery || err != nil {
89 89
 		if r.readTotal > 0 {
90
-			fmt.Fprintf(r.output, r.template, HumanSize(int64(r.readProgress)), HumanSize(int64(r.readTotal)), fmt.Sprintf("%2.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
90
+			fmt.Fprintf(r.output, r.template, HumanSize(int64(r.readProgress)), HumanSize(int64(r.readTotal)), fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
91 91
 		} else {
92 92
 			fmt.Fprintf(r.output, r.template, r.readProgress, "?", "n/a")
93 93
 		}
... ...
@@ -106,7 +106,7 @@ func (r *progressReader) Close() error {
106 106
 func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte, sf *StreamFormatter) *progressReader {
107 107
 	tpl := string(template)
108 108
 	if tpl == "" {
109
-		tpl = string(sf.FormatProgress("", "%v/%v (%v)"))
109
+		tpl = string(sf.FormatProgress("", "%8v/%v (%v)"))
110 110
 	}
111 111
 	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, tpl, sf}
112 112
 }
... ...
@@ -147,7 +147,7 @@ func HumanSize(size int64) string {
147 147
 		sizef = sizef / 1000.0
148 148
 		i++
149 149
 	}
150
-	return fmt.Sprintf("%5.4g %s", sizef, units[i])
150
+	return fmt.Sprintf("%.4g %s", sizef, units[i])
151 151
 }
152 152
 
153 153
 func Trunc(s string, maxlen int) string {
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"path"
8 8
 	"strings"
9 9
 	"testing"
10
+	"github.com/dotcloud/docker/utils"
10 11
 )
11 12
 
12 13
 // This file contains utility functions for docker's unit test suite.
... ...
@@ -15,14 +16,40 @@ import (
15 15
 
16 16
 // Create a temporary runtime suitable for unit testing.
17 17
 // Call t.Fatal() at the first error.
18
-func mkRuntime(t *testing.T) *Runtime {
18
+func mkRuntime(f Fataler) *Runtime {
19 19
 	runtime, err := newTestRuntime()
20 20
 	if err != nil {
21
-		t.Fatal(err)
21
+		f.Fatal(err)
22 22
 	}
23 23
 	return runtime
24 24
 }
25 25
 
26
+// A common interface to access the Fatal method of
27
+// both testing.B and testing.T.
28
+type Fataler interface {
29
+	Fatal(args ...interface{})
30
+}
31
+
32
+func newTestRuntime() (*Runtime, error) {
33
+	root, err := ioutil.TempDir("", "docker-test")
34
+	if err != nil {
35
+		return nil, err
36
+	}
37
+	if err := os.Remove(root); err != nil {
38
+		return nil, err
39
+	}
40
+	if err := utils.CopyDirectory(unitTestStoreBase, root); err != nil {
41
+		return nil, err
42
+	}
43
+
44
+	runtime, err := NewRuntimeFromDirectory(root, false)
45
+	if err != nil {
46
+		return nil, err
47
+	}
48
+	runtime.UpdateCapabilities(true)
49
+	return runtime, nil
50
+}
51
+
26 52
 // Write `content` to the file at path `dst`, creating it if necessary,
27 53
 // as well as any missing directories.
28 54
 // The file is truncated if it already exists.