github.com/nf/docker@v1.8.1/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  }