// Part of #14845 func (s *DockerSuite) TestExecResizeImmediatelyAfterExecStart(c *check.C) { name := "exec_resize_test" dockerCmd(c, "run", "-d", "-i", "-t", "--name", name, "--restart", "always", "busybox", "/bin/sh") testExecResize := func() error { data := map[string]interface{}{ "AttachStdin": true, "Cmd": []string{"/bin/sh"}, } uri := fmt.Sprintf("/containers/%s/exec", name) status, body, err := request.SockRequest("POST", uri, data, daemonHost()) if err != nil { return err } if status != http.StatusCreated { return fmt.Errorf("POST %s is expected to return %d, got %d", uri, http.StatusCreated, status) } out := map[string]string{} err = json.Unmarshal(body, &out) if err != nil { return fmt.Errorf("ExecCreate returned invalid json. Error: %q", err.Error()) } execID := out["Id"] if len(execID) < 1 { return fmt.Errorf("ExecCreate got invalid execID") } payload := bytes.NewBufferString(`{"Tty":true}`) conn, _, err := request.SockRequestHijack("POST", fmt.Sprintf("/exec/%s/start", execID), payload, "application/json", daemonHost()) if err != nil { return fmt.Errorf("Failed to start the exec: %q", err.Error()) } defer conn.Close() _, rc, err := request.SockRequestRaw("POST", fmt.Sprintf("/exec/%s/resize?h=24&w=80", execID), nil, "text/plain", daemonHost()) // It's probably a panic of the daemon if io.ErrUnexpectedEOF is returned. if err == io.ErrUnexpectedEOF { return fmt.Errorf("The daemon might have crashed.") } if err == nil { rc.Close() } // We only interested in the io.ErrUnexpectedEOF error, so we return nil otherwise. return nil } // The panic happens when daemon.ContainerExecStart is called but the // container.Exec is not called. // Because the panic is not 100% reproducible, we send the requests concurrently // to increase the probability that the problem is triggered. var ( n = 10 ch = make(chan error, n) wg sync.WaitGroup ) for i := 0; i < n; i++ { wg.Add(1) go func() { defer wg.Done() if err := testExecResize(); err != nil { ch <- err } }() } wg.Wait() select { case err := <-ch: c.Fatal(err.Error()) default: } }
func (s *DockerSuite) TestPostContainersAttach(c *check.C) { testRequires(c, DaemonIsLinux) expectSuccess := func(conn net.Conn, br *bufio.Reader, stream string, tty bool) { defer conn.Close() expected := []byte("success") _, err := conn.Write(expected) c.Assert(err, checker.IsNil) conn.SetReadDeadline(time.Now().Add(time.Second)) lenHeader := 0 if !tty { lenHeader = 8 } actual := make([]byte, len(expected)+lenHeader) _, err = io.ReadFull(br, actual) c.Assert(err, checker.IsNil) if !tty { fdMap := map[string]byte{ "stdin": 0, "stdout": 1, "stderr": 2, } c.Assert(actual[0], checker.Equals, fdMap[stream]) } c.Assert(actual[lenHeader:], checker.DeepEquals, expected, check.Commentf("Attach didn't return the expected data from %s", stream)) } expectTimeout := func(conn net.Conn, br *bufio.Reader, stream string) { defer conn.Close() _, err := conn.Write([]byte{'t'}) c.Assert(err, checker.IsNil) conn.SetReadDeadline(time.Now().Add(time.Second)) actual := make([]byte, 1) _, err = io.ReadFull(br, actual) opErr, ok := err.(*net.OpError) c.Assert(ok, checker.Equals, true, check.Commentf("Error is expected to be *net.OpError, got %v", err)) c.Assert(opErr.Timeout(), checker.Equals, true, check.Commentf("Read from %s is expected to timeout", stream)) } // Create a container that only emits stdout. cid, _ := dockerCmd(c, "run", "-di", "busybox", "cat") cid = strings.TrimSpace(cid) // Attach to the container's stdout stream. conn, br, err := request.SockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stdout=1", nil, "text/plain", daemonHost()) c.Assert(err, checker.IsNil) // Check if the data from stdout can be received. expectSuccess(conn, br, "stdout", false) // Attach to the container's stderr stream. conn, br, err = request.SockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stderr=1", nil, "text/plain", daemonHost()) c.Assert(err, checker.IsNil) // Since the container only emits stdout, attaching to stderr should return nothing. expectTimeout(conn, br, "stdout") // Test the similar functions of the stderr stream. cid, _ = dockerCmd(c, "run", "-di", "busybox", "/bin/sh", "-c", "cat >&2") cid = strings.TrimSpace(cid) conn, br, err = request.SockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stderr=1", nil, "text/plain", daemonHost()) c.Assert(err, checker.IsNil) expectSuccess(conn, br, "stderr", false) conn, br, err = request.SockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stdout=1", nil, "text/plain", daemonHost()) c.Assert(err, checker.IsNil) expectTimeout(conn, br, "stderr") // Test with tty. cid, _ = dockerCmd(c, "run", "-dit", "busybox", "/bin/sh", "-c", "cat >&2") cid = strings.TrimSpace(cid) // Attach to stdout only. conn, br, err = request.SockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stdout=1", nil, "text/plain", daemonHost()) c.Assert(err, checker.IsNil) expectSuccess(conn, br, "stdout", true) // Attach without stdout stream. conn, br, err = request.SockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stderr=1", nil, "text/plain", daemonHost()) c.Assert(err, checker.IsNil) // Nothing should be received because both the stdout and stderr of the container will be // sent to the client as stdout when tty is enabled. expectTimeout(conn, br, "stdout") // Test the client API // Make sure we don't see "hello" if Logs is false client, err := client.NewEnvClient() c.Assert(err, checker.IsNil) cid, _ = dockerCmd(c, "run", "-di", "busybox", "/bin/sh", "-c", "echo hello; cat") cid = strings.TrimSpace(cid) attachOpts := types.ContainerAttachOptions{ Stream: true, Stdin: true, Stdout: true, } resp, err := client.ContainerAttach(context.Background(), cid, attachOpts) c.Assert(err, checker.IsNil) expectSuccess(resp.Conn, resp.Reader, "stdout", false) // Make sure we do see "hello" if Logs is true attachOpts.Logs = true resp, err = client.ContainerAttach(context.Background(), cid, attachOpts) c.Assert(err, checker.IsNil) defer resp.Conn.Close() resp.Conn.SetReadDeadline(time.Now().Add(time.Second)) _, err = resp.Conn.Write([]byte("success")) c.Assert(err, checker.IsNil) actualStdout := new(bytes.Buffer) actualStderr := new(bytes.Buffer) stdcopy.StdCopy(actualStdout, actualStderr, resp.Reader) c.Assert(actualStdout.Bytes(), checker.DeepEquals, []byte("hello\nsuccess"), check.Commentf("Attach didn't return the expected data from stdout")) }