This is part of an effort to break apart the legacy server package. Help wanted!
Docker-DCO-1.1-Signed-off-by: Solomon Hykes <solomon@docker.com> (github: shykes)
| ... | ... |
@@ -683,6 +683,11 @@ func (b *buildFile) run(c *daemon.Container) error {
|
| 683 | 683 |
var errCh chan error |
| 684 | 684 |
if b.verbose {
|
| 685 | 685 |
errCh = utils.Go(func() error {
|
| 686 |
+ // FIXME: call the 'attach' job so that daemon.Attach can be made private |
|
| 687 |
+ // |
|
| 688 |
+ // FIXME (LK4D4): Also, maybe makes sense to call "logs" job, it is like attach |
|
| 689 |
+ // but without hijacking for stdin. Also, with attach there can be race |
|
| 690 |
+ // condition because of some output already was printed before it. |
|
| 686 | 691 |
return <-b.daemon.Attach(c, nil, nil, b.outStream, b.errStream) |
| 687 | 692 |
}) |
| 688 | 693 |
} |
| ... | ... |
@@ -1,11 +1,122 @@ |
| 1 | 1 |
package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "encoding/json" |
|
| 5 |
+ "fmt" |
|
| 4 | 6 |
"io" |
| 7 |
+ "os" |
|
| 8 |
+ "time" |
|
| 5 | 9 |
|
| 10 |
+ "github.com/docker/docker/engine" |
|
| 6 | 11 |
"github.com/docker/docker/utils" |
| 7 | 12 |
) |
| 8 | 13 |
|
| 14 |
+func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status {
|
|
| 15 |
+ if len(job.Args) != 1 {
|
|
| 16 |
+ return job.Errorf("Usage: %s CONTAINER\n", job.Name)
|
|
| 17 |
+ } |
|
| 18 |
+ |
|
| 19 |
+ var ( |
|
| 20 |
+ name = job.Args[0] |
|
| 21 |
+ logs = job.GetenvBool("logs")
|
|
| 22 |
+ stream = job.GetenvBool("stream")
|
|
| 23 |
+ stdin = job.GetenvBool("stdin")
|
|
| 24 |
+ stdout = job.GetenvBool("stdout")
|
|
| 25 |
+ stderr = job.GetenvBool("stderr")
|
|
| 26 |
+ ) |
|
| 27 |
+ |
|
| 28 |
+ container := daemon.Get(name) |
|
| 29 |
+ if container == nil {
|
|
| 30 |
+ return job.Errorf("No such container: %s", name)
|
|
| 31 |
+ } |
|
| 32 |
+ |
|
| 33 |
+ //logs |
|
| 34 |
+ if logs {
|
|
| 35 |
+ cLog, err := container.ReadLog("json")
|
|
| 36 |
+ if err != nil && os.IsNotExist(err) {
|
|
| 37 |
+ // Legacy logs |
|
| 38 |
+ utils.Debugf("Old logs format")
|
|
| 39 |
+ if stdout {
|
|
| 40 |
+ cLog, err := container.ReadLog("stdout")
|
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ utils.Errorf("Error reading logs (stdout): %s", err)
|
|
| 43 |
+ } else if _, err := io.Copy(job.Stdout, cLog); err != nil {
|
|
| 44 |
+ utils.Errorf("Error streaming logs (stdout): %s", err)
|
|
| 45 |
+ } |
|
| 46 |
+ } |
|
| 47 |
+ if stderr {
|
|
| 48 |
+ cLog, err := container.ReadLog("stderr")
|
|
| 49 |
+ if err != nil {
|
|
| 50 |
+ utils.Errorf("Error reading logs (stderr): %s", err)
|
|
| 51 |
+ } else if _, err := io.Copy(job.Stderr, cLog); err != nil {
|
|
| 52 |
+ utils.Errorf("Error streaming logs (stderr): %s", err)
|
|
| 53 |
+ } |
|
| 54 |
+ } |
|
| 55 |
+ } else if err != nil {
|
|
| 56 |
+ utils.Errorf("Error reading logs (json): %s", err)
|
|
| 57 |
+ } else {
|
|
| 58 |
+ dec := json.NewDecoder(cLog) |
|
| 59 |
+ for {
|
|
| 60 |
+ l := &utils.JSONLog{}
|
|
| 61 |
+ |
|
| 62 |
+ if err := dec.Decode(l); err == io.EOF {
|
|
| 63 |
+ break |
|
| 64 |
+ } else if err != nil {
|
|
| 65 |
+ utils.Errorf("Error streaming logs: %s", err)
|
|
| 66 |
+ break |
|
| 67 |
+ } |
|
| 68 |
+ if l.Stream == "stdout" && stdout {
|
|
| 69 |
+ fmt.Fprintf(job.Stdout, "%s", l.Log) |
|
| 70 |
+ } |
|
| 71 |
+ if l.Stream == "stderr" && stderr {
|
|
| 72 |
+ fmt.Fprintf(job.Stderr, "%s", l.Log) |
|
| 73 |
+ } |
|
| 74 |
+ } |
|
| 75 |
+ } |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ //stream |
|
| 79 |
+ if stream {
|
|
| 80 |
+ var ( |
|
| 81 |
+ cStdin io.ReadCloser |
|
| 82 |
+ cStdout, cStderr io.Writer |
|
| 83 |
+ cStdinCloser io.Closer |
|
| 84 |
+ ) |
|
| 85 |
+ |
|
| 86 |
+ if stdin {
|
|
| 87 |
+ r, w := io.Pipe() |
|
| 88 |
+ go func() {
|
|
| 89 |
+ defer w.Close() |
|
| 90 |
+ defer utils.Debugf("Closing buffered stdin pipe")
|
|
| 91 |
+ io.Copy(w, job.Stdin) |
|
| 92 |
+ }() |
|
| 93 |
+ cStdin = r |
|
| 94 |
+ cStdinCloser = job.Stdin |
|
| 95 |
+ } |
|
| 96 |
+ if stdout {
|
|
| 97 |
+ cStdout = job.Stdout |
|
| 98 |
+ } |
|
| 99 |
+ if stderr {
|
|
| 100 |
+ cStderr = job.Stderr |
|
| 101 |
+ } |
|
| 102 |
+ |
|
| 103 |
+ <-daemon.Attach(container, cStdin, cStdinCloser, cStdout, cStderr) |
|
| 104 |
+ |
|
| 105 |
+ // If we are in stdinonce mode, wait for the process to end |
|
| 106 |
+ // otherwise, simply return |
|
| 107 |
+ if container.Config.StdinOnce && !container.Config.Tty {
|
|
| 108 |
+ container.State.WaitStop(-1 * time.Second) |
|
| 109 |
+ } |
|
| 110 |
+ } |
|
| 111 |
+ return engine.StatusOK |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+// FIXME: this should be private, and every outside subsystem |
|
| 115 |
+// should go through the "container_attach" job. But that would require |
|
| 116 |
+// that job to be properly documented, as well as the relationship betweem |
|
| 117 |
+// Attach and ContainerAttach. |
|
| 118 |
+// |
|
| 119 |
+// This method is in use by builder/builder.go. |
|
| 9 | 120 |
func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
|
| 10 | 121 |
var ( |
| 11 | 122 |
cStdout, cStderr io.ReadCloser |
| ... | ... |
@@ -105,7 +105,13 @@ type Daemon struct {
|
| 105 | 105 |
|
| 106 | 106 |
// Install installs daemon capabilities to eng. |
| 107 | 107 |
func (daemon *Daemon) Install(eng *engine.Engine) error {
|
| 108 |
- return eng.Register("container_inspect", daemon.ContainerInspect)
|
|
| 108 |
+ if err := eng.Register("container_inspect", daemon.ContainerInspect); err != nil {
|
|
| 109 |
+ return err |
|
| 110 |
+ } |
|
| 111 |
+ if err := eng.Register("attach", daemon.ContainerAttach); err != nil {
|
|
| 112 |
+ return err |
|
| 113 |
+ } |
|
| 114 |
+ return nil |
|
| 109 | 115 |
} |
| 110 | 116 |
|
| 111 | 117 |
// List returns an array of all containers registered in the daemon. |
| ... | ... |
@@ -798,105 +798,6 @@ func (srv *Server) ContainerLogs(job *engine.Job) engine.Status {
|
| 798 | 798 |
return engine.StatusOK |
| 799 | 799 |
} |
| 800 | 800 |
|
| 801 |
-func (srv *Server) ContainerAttach(job *engine.Job) engine.Status {
|
|
| 802 |
- if len(job.Args) != 1 {
|
|
| 803 |
- return job.Errorf("Usage: %s CONTAINER\n", job.Name)
|
|
| 804 |
- } |
|
| 805 |
- |
|
| 806 |
- var ( |
|
| 807 |
- name = job.Args[0] |
|
| 808 |
- logs = job.GetenvBool("logs")
|
|
| 809 |
- stream = job.GetenvBool("stream")
|
|
| 810 |
- stdin = job.GetenvBool("stdin")
|
|
| 811 |
- stdout = job.GetenvBool("stdout")
|
|
| 812 |
- stderr = job.GetenvBool("stderr")
|
|
| 813 |
- ) |
|
| 814 |
- |
|
| 815 |
- container := srv.daemon.Get(name) |
|
| 816 |
- if container == nil {
|
|
| 817 |
- return job.Errorf("No such container: %s", name)
|
|
| 818 |
- } |
|
| 819 |
- |
|
| 820 |
- //logs |
|
| 821 |
- if logs {
|
|
| 822 |
- cLog, err := container.ReadLog("json")
|
|
| 823 |
- if err != nil && os.IsNotExist(err) {
|
|
| 824 |
- // Legacy logs |
|
| 825 |
- utils.Debugf("Old logs format")
|
|
| 826 |
- if stdout {
|
|
| 827 |
- cLog, err := container.ReadLog("stdout")
|
|
| 828 |
- if err != nil {
|
|
| 829 |
- utils.Errorf("Error reading logs (stdout): %s", err)
|
|
| 830 |
- } else if _, err := io.Copy(job.Stdout, cLog); err != nil {
|
|
| 831 |
- utils.Errorf("Error streaming logs (stdout): %s", err)
|
|
| 832 |
- } |
|
| 833 |
- } |
|
| 834 |
- if stderr {
|
|
| 835 |
- cLog, err := container.ReadLog("stderr")
|
|
| 836 |
- if err != nil {
|
|
| 837 |
- utils.Errorf("Error reading logs (stderr): %s", err)
|
|
| 838 |
- } else if _, err := io.Copy(job.Stderr, cLog); err != nil {
|
|
| 839 |
- utils.Errorf("Error streaming logs (stderr): %s", err)
|
|
| 840 |
- } |
|
| 841 |
- } |
|
| 842 |
- } else if err != nil {
|
|
| 843 |
- utils.Errorf("Error reading logs (json): %s", err)
|
|
| 844 |
- } else {
|
|
| 845 |
- dec := json.NewDecoder(cLog) |
|
| 846 |
- for {
|
|
| 847 |
- l := &utils.JSONLog{}
|
|
| 848 |
- |
|
| 849 |
- if err := dec.Decode(l); err == io.EOF {
|
|
| 850 |
- break |
|
| 851 |
- } else if err != nil {
|
|
| 852 |
- utils.Errorf("Error streaming logs: %s", err)
|
|
| 853 |
- break |
|
| 854 |
- } |
|
| 855 |
- if l.Stream == "stdout" && stdout {
|
|
| 856 |
- fmt.Fprintf(job.Stdout, "%s", l.Log) |
|
| 857 |
- } |
|
| 858 |
- if l.Stream == "stderr" && stderr {
|
|
| 859 |
- fmt.Fprintf(job.Stderr, "%s", l.Log) |
|
| 860 |
- } |
|
| 861 |
- } |
|
| 862 |
- } |
|
| 863 |
- } |
|
| 864 |
- |
|
| 865 |
- //stream |
|
| 866 |
- if stream {
|
|
| 867 |
- var ( |
|
| 868 |
- cStdin io.ReadCloser |
|
| 869 |
- cStdout, cStderr io.Writer |
|
| 870 |
- cStdinCloser io.Closer |
|
| 871 |
- ) |
|
| 872 |
- |
|
| 873 |
- if stdin {
|
|
| 874 |
- r, w := io.Pipe() |
|
| 875 |
- go func() {
|
|
| 876 |
- defer w.Close() |
|
| 877 |
- defer utils.Debugf("Closing buffered stdin pipe")
|
|
| 878 |
- io.Copy(w, job.Stdin) |
|
| 879 |
- }() |
|
| 880 |
- cStdin = r |
|
| 881 |
- cStdinCloser = job.Stdin |
|
| 882 |
- } |
|
| 883 |
- if stdout {
|
|
| 884 |
- cStdout = job.Stdout |
|
| 885 |
- } |
|
| 886 |
- if stderr {
|
|
| 887 |
- cStderr = job.Stderr |
|
| 888 |
- } |
|
| 889 |
- |
|
| 890 |
- <-srv.daemon.Attach(container, cStdin, cStdinCloser, cStdout, cStderr) |
|
| 891 |
- |
|
| 892 |
- // If we are in stdinonce mode, wait for the process to end |
|
| 893 |
- // otherwise, simply return |
|
| 894 |
- if container.Config.StdinOnce && !container.Config.Tty {
|
|
| 895 |
- container.State.WaitStop(-1 * time.Second) |
|
| 896 |
- } |
|
| 897 |
- } |
|
| 898 |
- return engine.StatusOK |
|
| 899 |
-} |
|
| 900 | 801 |
|
| 901 | 802 |
func (srv *Server) ContainerCopy(job *engine.Job) engine.Status {
|
| 902 | 803 |
if len(job.Args) != 2 {
|
| ... | ... |
@@ -105,7 +105,6 @@ func InitServer(job *engine.Job) engine.Status {
|
| 105 | 105 |
"history": srv.ImageHistory, |
| 106 | 106 |
"viz": srv.ImagesViz, |
| 107 | 107 |
"container_copy": srv.ContainerCopy, |
| 108 |
- "attach": srv.ContainerAttach, |
|
| 109 | 108 |
"logs": srv.ContainerLogs, |
| 110 | 109 |
"changes": srv.ContainerChanges, |
| 111 | 110 |
"top": srv.ContainerTop, |