github.com/vikstrous/docker@v1.8.2/integration-cli/docker_api_attach_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "io" 6 "net/http" 7 "net/http/httputil" 8 "strings" 9 "time" 10 11 "github.com/go-check/check" 12 "golang.org/x/net/websocket" 13 ) 14 15 func (s *DockerSuite) TestGetContainersAttachWebsocket(c *check.C) { 16 out, _ := dockerCmd(c, "run", "-dit", "busybox", "cat") 17 18 rwc, err := sockConn(time.Duration(10 * time.Second)) 19 if err != nil { 20 c.Fatal(err) 21 } 22 23 cleanedContainerID := strings.TrimSpace(out) 24 config, err := websocket.NewConfig( 25 "/containers/"+cleanedContainerID+"/attach/ws?stream=1&stdin=1&stdout=1&stderr=1", 26 "http://localhost", 27 ) 28 if err != nil { 29 c.Fatal(err) 30 } 31 32 ws, err := websocket.NewClient(config, rwc) 33 if err != nil { 34 c.Fatal(err) 35 } 36 defer ws.Close() 37 38 expected := []byte("hello") 39 actual := make([]byte, len(expected)) 40 41 outChan := make(chan error) 42 go func() { 43 _, err := ws.Read(actual) 44 outChan <- err 45 close(outChan) 46 }() 47 48 inChan := make(chan error) 49 go func() { 50 _, err := ws.Write(expected) 51 inChan <- err 52 close(inChan) 53 }() 54 55 select { 56 case err := <-inChan: 57 if err != nil { 58 c.Fatal(err) 59 } 60 case <-time.After(5 * time.Second): 61 c.Fatal("Timeout writing to ws") 62 } 63 64 select { 65 case err := <-outChan: 66 if err != nil { 67 c.Fatal(err) 68 } 69 case <-time.After(5 * time.Second): 70 c.Fatal("Timeout reading from ws") 71 } 72 73 if !bytes.Equal(expected, actual) { 74 c.Fatal("Expected output on websocket to match input") 75 } 76 } 77 78 // regression gh14320 79 func (s *DockerSuite) TestPostContainersAttachContainerNotFound(c *check.C) { 80 status, body, err := sockRequest("POST", "/containers/doesnotexist/attach", nil) 81 c.Assert(status, check.Equals, http.StatusNotFound) 82 c.Assert(err, check.IsNil) 83 expected := "no such id: doesnotexist\n" 84 if !strings.Contains(string(body), expected) { 85 c.Fatalf("Expected response body to contain %q", expected) 86 } 87 } 88 89 func (s *DockerSuite) TestGetContainersWsAttachContainerNotFound(c *check.C) { 90 status, body, err := sockRequest("GET", "/containers/doesnotexist/attach/ws", nil) 91 c.Assert(status, check.Equals, http.StatusNotFound) 92 c.Assert(err, check.IsNil) 93 expected := "no such id: doesnotexist\n" 94 if !strings.Contains(string(body), expected) { 95 c.Fatalf("Expected response body to contain %q", expected) 96 } 97 } 98 99 func (s *DockerSuite) TestPostContainersAttach(c *check.C) { 100 out, _ := dockerCmd(c, "run", "-dit", "busybox", "cat") 101 102 r, w := io.Pipe() 103 defer r.Close() 104 defer w.Close() 105 106 conn, err := sockConn(time.Duration(10 * time.Second)) 107 c.Assert(err, check.IsNil) 108 109 containerID := strings.TrimSpace(out) 110 111 req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{})) 112 c.Assert(err, check.IsNil) 113 114 client := httputil.NewClientConn(conn, nil) 115 defer client.Close() 116 117 // Do POST attach request 118 resp, err := client.Do(req) 119 c.Assert(resp.StatusCode, check.Equals, http.StatusOK) 120 // If we check the err, we get a ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"} 121 // This means that the remote requested this be the last request serviced, is this okay? 122 123 // Test read and write to the attached container 124 expected := []byte("hello") 125 actual := make([]byte, len(expected)) 126 127 outChan := make(chan error) 128 go func() { 129 _, err := r.Read(actual) 130 outChan <- err 131 close(outChan) 132 }() 133 134 inChan := make(chan error) 135 go func() { 136 _, err := w.Write(expected) 137 inChan <- err 138 close(inChan) 139 }() 140 141 select { 142 case err := <-inChan: 143 c.Assert(err, check.IsNil) 144 case <-time.After(5 * time.Second): 145 c.Fatal("Timeout writing to stdout") 146 } 147 148 select { 149 case err := <-outChan: 150 c.Assert(err, check.IsNil) 151 case <-time.After(5 * time.Second): 152 c.Fatal("Timeout reading from stdin") 153 } 154 155 if !bytes.Equal(expected, actual) { 156 c.Fatal("Expected output to match input") 157 } 158 159 resp.Body.Close() 160 } 161 162 func (s *DockerSuite) TestPostContainersAttachStderr(c *check.C) { 163 out, _ := dockerCmd(c, "run", "-dit", "busybox", "/bin/sh", "-c", "cat >&2") 164 165 r, w := io.Pipe() 166 defer r.Close() 167 defer w.Close() 168 169 conn, err := sockConn(time.Duration(10 * time.Second)) 170 c.Assert(err, check.IsNil) 171 172 containerID := strings.TrimSpace(out) 173 174 req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{})) 175 c.Assert(err, check.IsNil) 176 177 client := httputil.NewClientConn(conn, nil) 178 defer client.Close() 179 180 // Do POST attach request 181 resp, err := client.Do(req) 182 c.Assert(resp.StatusCode, check.Equals, http.StatusOK) 183 // If we check the err, we get a ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"} 184 // This means that the remote requested this be the last request serviced, is this okay? 185 186 // Test read and write to the attached container 187 expected := []byte("hello") 188 actual := make([]byte, len(expected)) 189 190 outChan := make(chan error) 191 go func() { 192 _, err := r.Read(actual) 193 outChan <- err 194 close(outChan) 195 }() 196 197 inChan := make(chan error) 198 go func() { 199 _, err := w.Write(expected) 200 inChan <- err 201 close(inChan) 202 }() 203 204 select { 205 case err := <-inChan: 206 c.Assert(err, check.IsNil) 207 case <-time.After(5 * time.Second): 208 c.Fatal("Timeout writing to stdout") 209 } 210 211 select { 212 case err := <-outChan: 213 c.Assert(err, check.IsNil) 214 case <-time.After(5 * time.Second): 215 c.Fatal("Timeout reading from stdin") 216 } 217 218 if !bytes.Equal(expected, actual) { 219 c.Fatal("Expected output to match input") 220 } 221 222 resp.Body.Close() 223 }