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