... | ... |
@@ -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 |