Browse code

refactoring run/attach/logs

Victor Vieux authored on 2013/05/02 12:07:06
Showing 3 changed files
... ...
@@ -6,7 +6,6 @@ import (
6 6
 	"fmt"
7 7
 	"github.com/gorilla/mux"
8 8
 	"io"
9
-	"io/ioutil"
10 9
 	"log"
11 10
 	"net"
12 11
 	"net/http"
... ...
@@ -73,7 +72,7 @@ func ListenAndServe(addr string, rtime *Runtime) error {
73 73
 			}
74 74
 			defer file.Close()
75 75
 
76
-			fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\n\r\n")
76
+			fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
77 77
 			// Stream the entire contents of the container (basically a volatile snapshot)
78 78
 			if _, err := io.Copy(file, data); err != nil {
79 79
 				fmt.Fprintln(file, "Error: "+err.Error())
... ...
@@ -207,52 +206,6 @@ func ListenAndServe(addr string, rtime *Runtime) error {
207 207
 		}
208 208
 	})
209 209
 
210
-	r.Path("/containers/{name:.*}/logs").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
211
-		log.Println(r.Method, r.RequestURI)
212
-		vars := mux.Vars(r)
213
-		name := vars["name"]
214
-
215
-		if container := rtime.Get(name); container != nil {
216
-			var out ApiLogs
217
-
218
-			logStdout, err := container.ReadLog("stdout")
219
-			if err != nil {
220
-				http.Error(w, err.Error(), http.StatusInternalServerError)
221
-				return
222
-			}
223
-			logStderr, err := container.ReadLog("stderr")
224
-			if err != nil {
225
-				http.Error(w, err.Error(), http.StatusInternalServerError)
226
-				return
227
-			}
228
-
229
-			stdout, errStdout := ioutil.ReadAll(logStdout)
230
-			if errStdout != nil {
231
-				http.Error(w, errStdout.Error(), http.StatusInternalServerError)
232
-				return
233
-			} else {
234
-				out.Stdout = fmt.Sprintf("%s", stdout)
235
-			}
236
-			stderr, errStderr := ioutil.ReadAll(logStderr)
237
-			if errStderr != nil {
238
-				http.Error(w, errStderr.Error(), http.StatusInternalServerError)
239
-				return
240
-			} else {
241
-				out.Stderr = fmt.Sprintf("%s", stderr)
242
-			}
243
-
244
-			b, err := json.Marshal(out)
245
-			if err != nil {
246
-				http.Error(w, err.Error(), http.StatusInternalServerError)
247
-			} else {
248
-				w.Write(b)
249
-			}
250
-
251
-		} else {
252
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
253
-		}
254
-	})
255
-
256 210
 	r.Path("/containers/{name:.*}/changes").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
257 211
 		log.Println(r.Method, r.RequestURI)
258 212
 		vars := mux.Vars(r)
... ...
@@ -368,7 +321,7 @@ func ListenAndServe(addr string, rtime *Runtime) error {
368 368
 			return
369 369
 		}
370 370
 
371
-		b, err := json.Marshal(ApiCommit{img.ShortId()})
371
+		b, err := json.Marshal(ApiId{img.ShortId()})
372 372
 		if err != nil {
373 373
 			http.Error(w, err.Error(), http.StatusInternalServerError)
374 374
 		} else {
... ...
@@ -415,7 +368,7 @@ func ListenAndServe(addr string, rtime *Runtime) error {
415 415
 		}
416 416
 		defer file.Close()
417 417
 
418
-		fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n")
418
+		fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
419 419
 		if rtime.graph.LookupRemoteImage(name, rtime.authConfig) {
420 420
 			if err := rtime.graph.PullImage(file, name, rtime.authConfig); err != nil {
421 421
 				fmt.Fprintln(file, "Error: "+err.Error())
... ...
@@ -506,146 +459,21 @@ func ListenAndServe(addr string, rtime *Runtime) error {
506 506
 			return
507 507
 		}
508 508
 
509
-		conn, _, err := w.(http.Hijacker).Hijack()
510
-		if err != nil {
511
-			http.Error(w, err.Error(), http.StatusInternalServerError)
512
-			return
513
-		}
514
-		defer conn.Close()
515
-		file, err := conn.(*net.TCPConn).File()
516
-		if err != nil {
517
-			http.Error(w, err.Error(), http.StatusInternalServerError)
518
-			return
519
-		}
520
-		defer file.Close()
521
-
522
-		if config.Tty {
523
-			oldState, err := SetRawTerminal()
524
-			if err != nil {
525
-				if os.Getenv("DEBUG") != "" {
526
-					log.Printf("Can't set the terminal in raw mode: %s", err)
527
-				}
528
-			} else {
529
-				defer RestoreTerminal(oldState)
530
-			}
531
-		}
532
-
533
-		// Flush the options to make sure the client sets the raw mode
534
-		// or tell the client there is no options
535
-		conn.Write([]byte{})
536
-
537
-		fmt.Fprintln(file, "HTTP/1.1 201 Created\r\nContent-Type: application/json\r\n\r\n")
538 509
 		container, err := rtime.Create(&config)
539 510
 		if err != nil {
540
-			// If container not found, try to pull it
541 511
 			if rtime.graph.IsNotExist(err) {
542
-				fmt.Fprintf(file, "Image %s not found, trying to pull it from registry.\r\n", config.Image)
543
-				if rtime.graph.LookupRemoteImage(config.Image, rtime.authConfig) {
544
-					if err := rtime.graph.PullImage(file, config.Image, rtime.authConfig); err != nil {
545
-						fmt.Fprintln(file, "Error: "+err.Error())
546
-						return
547
-					}
548
-				} else if err := rtime.graph.PullRepository(file, config.Image, "", rtime.repositories, rtime.authConfig); err != nil {
549
-					fmt.Fprintln(file, "Error: "+err.Error())
550
-					return
551
-				}
552
-				if container, err = rtime.Create(&config); err != nil {
553
-					fmt.Fprintln(file, "Error: "+err.Error())
554
-					return
555
-				}
512
+				http.Error(w, "No such image: "+config.Image, http.StatusNotFound)
556 513
 			} else {
557
-				fmt.Fprintln(file, "Error: "+err.Error())
558
-				return
514
+				http.Error(w, err.Error(), http.StatusInternalServerError)
559 515
 			}
560
-		}
561
-		var (
562
-			cStdin           io.ReadCloser
563
-			cStdout, cStderr io.Writer
564
-		)
565
-		if config.AttachStdin {
566
-			r, w := io.Pipe()
567
-			go func() {
568
-				defer w.Close()
569
-				defer Debugf("Closing buffered stdin pipe")
570
-				io.Copy(w, file)
571
-			}()
572
-			cStdin = r
573
-		}
574
-		if config.AttachStdout {
575
-			cStdout = file
576
-		}
577
-		if config.AttachStderr {
578
-			cStderr = file // FIXME: api can't differentiate stdout from stderr
579
-		}
580
-
581
-		attachErr := container.Attach(cStdin, file, cStdout, cStderr)
582
-		Debugf("Starting\n")
583
-		if err := container.Start(); err != nil {
584
-			fmt.Fprintln(file, "Error: "+err.Error())
585 516
 			return
586 517
 		}
587
-		if cStdout == nil && cStderr == nil {
588
-			fmt.Fprintln(file, container.ShortId())
589
-		}
590
-		Debugf("Waiting for attach to return\n")
591
-		<-attachErr
592
-		// Expecting I/O pipe error, discarding
593
-
594
-		// If we are in stdinonce mode, wait for the process to end
595
-		// otherwise, simply return
596
-		if config.StdinOnce && !config.Tty {
597
-			container.Wait()
598
-		}
599
-	})
600
-
601
-	r.Path("/containers/{name:.*}/attach").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
602
-		log.Println(r.Method, r.RequestURI)
603
-		vars := mux.Vars(r)
604
-		name := vars["name"]
605
-		if container := rtime.Get(name); container != nil {
606
-			conn, _, err := w.(http.Hijacker).Hijack()
607
-			if err != nil {
608
-				http.Error(w, err.Error(), http.StatusInternalServerError)
609
-				return
610
-			}
611
-			defer conn.Close()
612
-			file, err := conn.(*net.TCPConn).File()
613
-			if err != nil {
614
-				http.Error(w, err.Error(), http.StatusInternalServerError)
615
-				return
616
-			}
617
-			defer file.Close()
618
-
619
-			if container.Config.Tty {
620
-				oldState, err := SetRawTerminal()
621
-				if err != nil {
622
-					if os.Getenv("DEBUG") != "" {
623
-						log.Printf("Can't set the terminal in raw mode: %s", err)
624
-					}
625
-				} else {
626
-					defer RestoreTerminal(oldState)
627
-				}
628
-
629
-			}
630
-
631
-			// Flush the options to make sure the client sets the raw mode
632
-			conn.Write([]byte{})
633
-
634
-			fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n")
635
-			r, w := io.Pipe()
636
-			go func() {
637
-				defer w.Close()
638
-				defer Debugf("Closing buffered stdin pipe")
639
-				io.Copy(w, file)
640
-			}()
641
-			cStdin := r
642
-
643
-			<-container.Attach(cStdin, nil, file, file)
644
-			// Expecting I/O pipe error, discarding                     
645 518
 
519
+		b, err := json.Marshal(ApiId{container.ShortId()})
520
+		if err != nil {
521
+			http.Error(w, err.Error(), http.StatusInternalServerError)
646 522
 		} else {
647
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
648
-			return
523
+			w.Write(b)
649 524
 		}
650 525
 	})
651 526
 
... ...
@@ -749,6 +577,99 @@ func ListenAndServe(addr string, rtime *Runtime) error {
749 749
 		}
750 750
 	})
751 751
 
752
+	r.Path("/containers/{name:.*}/attach").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
753
+		log.Println(r.Method, r.RequestURI)
754
+		if err := r.ParseForm(); err != nil {
755
+			http.Error(w, err.Error(), http.StatusInternalServerError)
756
+		}
757
+		logs := r.Form.Get("logs")
758
+		stream := r.Form.Get("stream")
759
+		stdin := r.Form.Get("stdin")
760
+		stdout := r.Form.Get("stdout")
761
+		stderr := r.Form.Get("stderr")
762
+		vars := mux.Vars(r)
763
+		name := vars["name"]
764
+
765
+		if container := rtime.Get(name); container != nil {
766
+			conn, _, err := w.(http.Hijacker).Hijack()
767
+			if err != nil {
768
+				http.Error(w, err.Error(), http.StatusInternalServerError)
769
+				return
770
+			}
771
+			defer conn.Close()
772
+			file, err := conn.(*net.TCPConn).File()
773
+			if err != nil {
774
+				http.Error(w, err.Error(), http.StatusInternalServerError)
775
+				return
776
+			}
777
+			defer file.Close()
778
+			if container.Config.Tty {
779
+				oldState, err := SetRawTerminal()
780
+				if err != nil {
781
+					if os.Getenv("DEBUG") != "" {
782
+						log.Printf("Can't set the terminal in raw mode: %s", err)
783
+					}
784
+				} else {
785
+					defer RestoreTerminal(oldState)
786
+				}
787
+
788
+			}
789
+			// Flush the options to make sure the client sets the raw mode
790
+			conn.Write([]byte{})
791
+
792
+			fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
793
+			//logs
794
+			if logs == "1" {
795
+				if stdout == "1" {
796
+					cLog, err := container.ReadLog("stdout")
797
+					if err != nil {
798
+						Debugf(err.Error())
799
+					} else if _, err := io.Copy(file, cLog); err != nil {
800
+						Debugf(err.Error())
801
+					}
802
+				}
803
+				if stderr == "1" {
804
+					cLog, err := container.ReadLog("stderr")
805
+					if err != nil {
806
+						Debugf(err.Error())
807
+					} else if _, err := io.Copy(file, cLog); err != nil {
808
+						Debugf(err.Error())
809
+					}
810
+				}
811
+			}
812
+
813
+			//stream
814
+			if stream == "1" {
815
+				var (
816
+					cStdin           io.ReadCloser
817
+					cStdout, cStderr io.Writer
818
+					cStdinCloser     io.Closer
819
+				)
820
+
821
+				if stdin == "1" {
822
+					r, w := io.Pipe()
823
+					go func() {
824
+						defer w.Close()
825
+						defer Debugf("Closing buffered stdin pipe")
826
+						io.Copy(w, file)
827
+					}()
828
+					cStdin = r
829
+					cStdinCloser = file
830
+				}
831
+				if stdout == "1" {
832
+					cStdout = file
833
+				}
834
+				if stderr == "1" {
835
+					cStderr = file
836
+				}
837
+
838
+				<-container.Attach(cStdin, cStdinCloser, cStdout, cStderr)
839
+			}
840
+		} else {
841
+			http.Error(w, "No such container: "+name, http.StatusNotFound)
842
+		}
843
+	})
844
+
752 845
 	r.Path("/containers/{name:.*}").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
753 846
 		log.Println(r.Method, r.RequestURI)
754 847
 		vars := mux.Vars(r)
... ...
@@ -30,15 +30,10 @@ type ApiContainers struct {
30 30
 	Status  string `json:",omitempty"`
31 31
 }
32 32
 
33
-type ApiCommit struct {
33
+type ApiId struct {
34 34
 	Id string
35 35
 }
36 36
 
37
-type ApiLogs struct {
38
-	Stdout string
39
-	Stderr string
40
-}
41
-
42 37
 type ApiPort struct {
43 38
 	Port string
44 39
 }
... ...
@@ -210,7 +210,7 @@ func CmdWait(args []string) error {
210 210
 		return nil
211 211
 	}
212 212
 	for _, name := range cmd.Args() {
213
-		body, err := call("POST", "/containers/"+name+"/wait")
213
+		body, _, err := call("POST", "/containers/"+name+"/wait", nil)
214 214
 		if err != nil {
215 215
 			fmt.Printf("%s", err)
216 216
 		} else {
... ...
@@ -236,7 +236,7 @@ func CmdVersion(args []string) error {
236 236
 		return nil
237 237
 	}
238 238
 
239
-	body, err := call("GET", "/version")
239
+	body, _, err := call("GET", "/version", nil)
240 240
 	if err != nil {
241 241
 		return err
242 242
 	}
... ...
@@ -266,7 +266,7 @@ func CmdInfo(args []string) error {
266 266
 		return nil
267 267
 	}
268 268
 
269
-	body, err := call("GET", "/info")
269
+	body, _, err := call("GET", "/info", nil)
270 270
 	if err != nil {
271 271
 		return err
272 272
 	}
... ...
@@ -295,7 +295,7 @@ func CmdStop(args []string) error {
295 295
 	}
296 296
 
297 297
 	for _, name := range args {
298
-		_, err := call("POST", "/containers/"+name+"/stop")
298
+		_, _, err := call("POST", "/containers/"+name+"/stop", nil)
299 299
 		if err != nil {
300 300
 			fmt.Printf("%s", err)
301 301
 		} else {
... ...
@@ -316,7 +316,7 @@ func CmdRestart(args []string) error {
316 316
 	}
317 317
 
318 318
 	for _, name := range args {
319
-		_, err := call("POST", "/containers/"+name+"/restart")
319
+		_, _, err := call("POST", "/containers/"+name+"/restart", nil)
320 320
 		if err != nil {
321 321
 			fmt.Printf("%s", err)
322 322
 		} else {
... ...
@@ -337,7 +337,7 @@ func CmdStart(args []string) error {
337 337
 	}
338 338
 
339 339
 	for _, name := range args {
340
-		_, err := call("POST", "/containers/"+name+"/start")
340
+		_, _, err := call("POST", "/containers/"+name+"/start", nil)
341 341
 		if err != nil {
342 342
 			fmt.Printf("%s", err)
343 343
 		} else {
... ...
@@ -356,9 +356,9 @@ func CmdInspect(args []string) error {
356 356
 		cmd.Usage()
357 357
 		return nil
358 358
 	}
359
-	obj, err := call("GET", "/containers/"+cmd.Arg(0))
359
+	obj, _, err := call("GET", "/containers/"+cmd.Arg(0), nil)
360 360
 	if err != nil {
361
-		obj, err = call("GET", "/images/"+cmd.Arg(0))
361
+		obj, _, err = call("GET", "/images/"+cmd.Arg(0), nil)
362 362
 		if err != nil {
363 363
 			return err
364 364
 		}
... ...
@@ -378,7 +378,7 @@ func CmdPort(args []string) error {
378 378
 	}
379 379
 	v := url.Values{}
380 380
 	v.Set("port", cmd.Arg(1))
381
-	body, err := call("GET", "/containers/"+cmd.Arg(0)+"/port?"+v.Encode())
381
+	body, _, err := call("GET", "/containers/"+cmd.Arg(0)+"/port?"+v.Encode(), nil)
382 382
 	if err != nil {
383 383
 		return err
384 384
 	}
... ...
@@ -404,7 +404,7 @@ func CmdRmi(args []string) error {
404 404
 	}
405 405
 
406 406
 	for _, name := range args {
407
-		_, err := call("DELETE", "/images/"+name)
407
+		_, _, err := call("DELETE", "/images/"+name, nil)
408 408
 		if err != nil {
409 409
 			fmt.Printf("%s", err)
410 410
 		} else {
... ...
@@ -424,7 +424,7 @@ func CmdHistory(args []string) error {
424 424
 		return nil
425 425
 	}
426 426
 
427
-	body, err := call("GET", "/images/"+cmd.Arg(0)+"/history")
427
+	body, _, err := call("GET", "/images/"+cmd.Arg(0)+"/history", nil)
428 428
 	if err != nil {
429 429
 		return err
430 430
 	}
... ...
@@ -455,7 +455,7 @@ func CmdRm(args []string) error {
455 455
 	}
456 456
 
457 457
 	for _, name := range args {
458
-		_, err := call("DELETE", "/containers/"+name)
458
+		_, _, err := call("DELETE", "/containers/"+name, nil)
459 459
 		if err != nil {
460 460
 			fmt.Printf("%s", err)
461 461
 		} else {
... ...
@@ -477,7 +477,7 @@ func CmdKill(args []string) error {
477 477
 	}
478 478
 
479 479
 	for _, name := range args {
480
-		_, err := call("POST", "/containers/"+name+"/kill")
480
+		_, _, err := call("POST", "/containers/"+name+"/kill", nil)
481 481
 		if err != nil {
482 482
 			fmt.Printf("%s", err)
483 483
 		} else {
... ...
@@ -504,7 +504,7 @@ func CmdImport(args []string) error {
504 504
 	v.Set("tag", tag)
505 505
 	v.Set("src", src)
506 506
 
507
-	err := callStream("POST", "/images?"+v.Encode(), nil, false)
507
+	err := hijack("POST", "/images?"+v.Encode(), false)
508 508
 	if err != nil {
509 509
 		return err
510 510
 	}
... ...
@@ -583,7 +583,7 @@ func CmdPull(args []string) error {
583 583
 		return nil
584 584
 	}
585 585
 
586
-	if err := callStream("POST", "/images/"+cmd.Arg(0)+"/pull", nil, false); err != nil {
586
+	if err := hijack("POST", "/images/"+cmd.Arg(0)+"/pull", false); err != nil {
587 587
 		return err
588 588
 	}
589 589
 
... ...
@@ -613,7 +613,7 @@ func CmdImages(args []string) error {
613 613
 		v.Set("all", "1")
614 614
 	}
615 615
 
616
-	body, err := call("GET", "/images?"+v.Encode())
616
+	body, _, err := call("GET", "/images?"+v.Encode(), nil)
617 617
 	if err != nil {
618 618
 		return err
619 619
 	}
... ...
@@ -670,7 +670,7 @@ func CmdPs(args []string) error {
670 670
 		v.Set("n", strconv.Itoa(*last))
671 671
 	}
672 672
 
673
-	body, err := call("GET", "/containers?"+v.Encode())
673
+	body, _, err := call("GET", "/containers?"+v.Encode(), nil)
674 674
 	if err != nil {
675 675
 		return err
676 676
 	}
... ...
@@ -715,12 +715,12 @@ func CmdCommit(args []string) error {
715 715
 	v.Set("tag", tag)
716 716
 	v.Set("comment", *flComment)
717 717
 
718
-	body, err := call("POST", "/containers/"+name+"/commit?"+v.Encode())
718
+	body, _, err := call("POST", "/containers/"+name+"/commit?"+v.Encode(), nil)
719 719
 	if err != nil {
720 720
 		return err
721 721
 	}
722 722
 
723
-	var out ApiCommit
723
+	var out ApiId
724 724
 	err = json.Unmarshal(body, &out)
725 725
 	if err != nil {
726 726
 		return err
... ...
@@ -741,7 +741,7 @@ func CmdExport(args []string) error {
741 741
 		return nil
742 742
 	}
743 743
 
744
-	if err := callStream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, false); err != nil {
744
+	if err := hijack("GET", "/containers/"+cmd.Arg(0)+"/export", false); err != nil {
745 745
 		return err
746 746
 	}
747 747
 	return nil
... ...
@@ -757,7 +757,7 @@ func CmdDiff(args []string) error {
757 757
 		return nil
758 758
 	}
759 759
 
760
-	body, err := call("GET", "/containers/"+cmd.Arg(0)+"/changes")
760
+	body, _, err := call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil)
761 761
 	if err != nil {
762 762
 		return err
763 763
 	}
... ...
@@ -782,19 +782,15 @@ func CmdLogs(args []string) error {
782 782
 		cmd.Usage()
783 783
 		return nil
784 784
 	}
785
-	body, err := call("GET", "/containers/"+cmd.Arg(0)+"/logs")
786
-	if err != nil {
787
-		return err
788
-	}
789 785
 
790
-	var out ApiLogs
791
-	err = json.Unmarshal(body, &out)
792
-	if err != nil {
786
+	v := url.Values{}
787
+	v.Set("logs", "1")
788
+	v.Set("stdout", "1")
789
+	v.Set("stderr", "1")
790
+
791
+	if err := hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), false); err != nil {
793 792
 		return err
794 793
 	}
795
-	fmt.Fprintln(os.Stdout, out.Stdout)
796
-	fmt.Fprintln(os.Stderr, out.Stderr)
797
-
798 794
 	return nil
799 795
 }
800 796
 
... ...
@@ -808,7 +804,7 @@ func CmdAttach(args []string) error {
808 808
 		return nil
809 809
 	}
810 810
 
811
-	body, err := call("GET", "/containers/"+cmd.Arg(0))
811
+	body, _, err := call("GET", "/containers/"+cmd.Arg(0), nil)
812 812
 	if err != nil {
813 813
 		return err
814 814
 	}
... ...
@@ -819,7 +815,14 @@ func CmdAttach(args []string) error {
819 819
 		return err
820 820
 	}
821 821
 
822
-	if err := callStream("POST", "/containers/"+cmd.Arg(0)+"/attach", nil, container.Config.Tty); err != nil {
822
+	v := url.Values{}
823
+	v.Set("logs", "1")
824
+	v.Set("stream", "1")
825
+	v.Set("stdout", "1")
826
+	v.Set("stderr", "1")
827
+	v.Set("stdin", "1")
828
+
829
+	if err := hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty); err != nil {
823 830
 		return err
824 831
 	}
825 832
 	return nil
... ...
@@ -903,7 +906,7 @@ func CmdTag(args []string) error {
903 903
 		v.Set("force", "1")
904 904
 	}
905 905
 
906
-	if err := callStream("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false); err != nil {
906
+	if _, _, err := call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil); err != nil {
907 907
 		return err
908 908
 	}
909 909
 	return nil
... ...
@@ -923,54 +926,105 @@ func CmdRun(args []string) error {
923 923
 		return nil
924 924
 	}
925 925
 
926
-	if err := callStream("POST", "/containers", *config, config.Tty); err != nil {
926
+	//create the container
927
+	body, statusCode, err := call("POST", "/containers", *config)
928
+
929
+	//if image not found try to pull it
930
+	if statusCode == 404 {
931
+		err = hijack("POST", "/images/"+config.Image+"/pull", false)
932
+		if err != nil {
933
+			return err
934
+		}
935
+		body, _, err = call("POST", "/containers", *config)
936
+	}
937
+	if err != nil {
927 938
 		return err
928 939
 	}
929
-	return nil
930
-}
931 940
 
932
-func call(method, path string) ([]byte, error) {
933
-	req, err := http.NewRequest(method, "http://0.0.0.0:4243"+path, nil)
941
+	var out ApiId
942
+	err = json.Unmarshal(body, &out)
934 943
 	if err != nil {
935
-		return nil, err
944
+		return err
936 945
 	}
937
-	if method == "POST" {
938
-		req.Header.Set("Content-Type", "plain/text")
946
+
947
+	v := url.Values{}
948
+	v.Set("logs", "1")
949
+	v.Set("stream", "1")
950
+
951
+	if config.AttachStdin {
952
+		v.Set("stdin", "1")
939 953
 	}
940
-	resp, err := http.DefaultClient.Do(req)
941
-	if err != nil {
942
-		return nil, err
954
+	if config.AttachStdout {
955
+		v.Set("stdout", "1")
943 956
 	}
944
-	defer resp.Body.Close()
945
-	body, err := ioutil.ReadAll(resp.Body)
957
+	if config.AttachStderr {
958
+		v.Set("stderr", "1")
959
+
960
+	}
961
+	/*
962
+		attach := Go(func() error {
963
+			err := hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty)
964
+			return err
965
+		})*/
966
+
967
+	//start the container
968
+	_, _, err = call("POST", "/containers/"+out.Id+"/start", nil)
946 969
 	if err != nil {
947
-		return nil, err
970
+		return err
948 971
 	}
949
-	if resp.StatusCode != 200 {
950
-		return nil, fmt.Errorf("error: %s", body)
972
+
973
+	if err := hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty); err != nil {
974
+		return err
951 975
 	}
952
-	return body, nil
953 976
 
977
+	/*
978
+		if err := <-attach; err != nil {
979
+			return err
980
+		}
981
+	*/
982
+
983
+	return nil
954 984
 }
955 985
 
956
-func callStream(method, path string, data interface{}, setRawTerminal bool) error {
957
-	var body io.Reader
986
+func call(method, path string, data interface{}) ([]byte, int, error) {
987
+	var params io.Reader
958 988
 	if data != nil {
959 989
 		buf, err := json.Marshal(data)
960 990
 		if err != nil {
961
-			return err
991
+			return nil, -1, err
962 992
 		}
963
-		body = bytes.NewBuffer(buf)
993
+		params = bytes.NewBuffer(buf)
964 994
 	}
965
-	req, err := http.NewRequest(method, path, body)
995
+	req, err := http.NewRequest(method, "http://0.0.0.0:4243"+path, params)
966 996
 	if err != nil {
967
-		return err
997
+		return nil, -1, err
968 998
 	}
969
-
970 999
 	if data != nil {
971 1000
 		req.Header.Set("Content-Type", "application/json")
1001
+	} else if method == "POST" {
1002
+		req.Header.Set("Content-Type", "plain/text")
972 1003
 	}
1004
+	resp, err := http.DefaultClient.Do(req)
1005
+	if err != nil {
1006
+		return nil, -1, err
1007
+	}
1008
+	defer resp.Body.Close()
1009
+	body, err := ioutil.ReadAll(resp.Body)
1010
+	if err != nil {
1011
+		return nil, -1, err
1012
+	}
1013
+	if resp.StatusCode != 200 {
1014
+		return nil, resp.StatusCode, fmt.Errorf("error: %s", body)
1015
+	}
1016
+	return body, resp.StatusCode, nil
1017
+
1018
+}
973 1019
 
1020
+func hijack(method, path string, setRawTerminal bool) error {
1021
+	req, err := http.NewRequest(method, path, nil)
1022
+	if err != nil {
1023
+		return err
1024
+	}
974 1025
 	dial, err := net.Dial("tcp", "0.0.0.0:4243")
975 1026
 	if err != nil {
976 1027
 		return err