github.com/rumpl/bof@v23.0.0-rc.2+incompatible/integration-cli/docker_cli_attach_unix_test.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  package main
     5  
     6  import (
     7  	"bufio"
     8  	"io"
     9  	"os/exec"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/creack/pty"
    15  	"gotest.tools/v3/assert"
    16  )
    17  
    18  // #9860 Make sure attach ends when container ends (with no errors)
    19  func (s *DockerCLIAttachSuite) TestAttachClosedOnContainerStop(c *testing.T) {
    20  	testRequires(c, testEnv.IsLocalDaemon)
    21  
    22  	out, _ := dockerCmd(c, "run", "-dti", "busybox", "/bin/sh", "-c", `trap 'exit 0' SIGTERM; while true; do sleep 1; done`)
    23  
    24  	id := strings.TrimSpace(out)
    25  	assert.NilError(c, waitRun(id))
    26  
    27  	pty, tty, err := pty.Open()
    28  	assert.NilError(c, err)
    29  
    30  	attachCmd := exec.Command(dockerBinary, "attach", id)
    31  	attachCmd.Stdin = tty
    32  	attachCmd.Stdout = tty
    33  	attachCmd.Stderr = tty
    34  	err = attachCmd.Start()
    35  	assert.NilError(c, err)
    36  
    37  	errChan := make(chan error, 1)
    38  	go func() {
    39  		time.Sleep(300 * time.Millisecond)
    40  		defer close(errChan)
    41  		// Container is waiting for us to signal it to stop
    42  		dockerCmd(c, "stop", id)
    43  		// And wait for the attach command to end
    44  		errChan <- attachCmd.Wait()
    45  	}()
    46  
    47  	// Wait for the docker to end (should be done by the
    48  	// stop command in the go routine)
    49  	dockerCmd(c, "wait", id)
    50  
    51  	select {
    52  	case err := <-errChan:
    53  		tty.Close()
    54  		out, _ := io.ReadAll(pty)
    55  		assert.Assert(c, err == nil, "out: %v", string(out))
    56  	case <-time.After(attachWait):
    57  		c.Fatal("timed out without attach returning")
    58  	}
    59  }
    60  
    61  func (s *DockerCLIAttachSuite) TestAttachAfterDetach(c *testing.T) {
    62  	name := "detachtest"
    63  
    64  	cpty, tty, err := pty.Open()
    65  	assert.NilError(c, err, "Could not open pty: %v", err)
    66  	cmd := exec.Command(dockerBinary, "run", "-ti", "--name", name, "busybox")
    67  	cmd.Stdin = tty
    68  	cmd.Stdout = tty
    69  	cmd.Stderr = tty
    70  
    71  	cmdExit := make(chan error, 1)
    72  	go func() {
    73  		cmdExit <- cmd.Run()
    74  		close(cmdExit)
    75  	}()
    76  
    77  	assert.Assert(c, waitRun(name) == nil)
    78  
    79  	cpty.Write([]byte{16})
    80  	time.Sleep(100 * time.Millisecond)
    81  	cpty.Write([]byte{17})
    82  
    83  	select {
    84  	case <-cmdExit:
    85  	case <-time.After(5 * time.Second):
    86  		c.Fatal("timeout while detaching")
    87  	}
    88  
    89  	cpty, tty, err = pty.Open()
    90  	assert.NilError(c, err, "Could not open pty: %v", err)
    91  
    92  	cmd = exec.Command(dockerBinary, "attach", name)
    93  	cmd.Stdin = tty
    94  	cmd.Stdout = tty
    95  	cmd.Stderr = tty
    96  
    97  	err = cmd.Start()
    98  	assert.NilError(c, err)
    99  	defer cmd.Process.Kill()
   100  
   101  	bytes := make([]byte, 10)
   102  	var nBytes int
   103  	readErr := make(chan error, 1)
   104  
   105  	go func() {
   106  		time.Sleep(500 * time.Millisecond)
   107  		cpty.Write([]byte("\n"))
   108  		time.Sleep(500 * time.Millisecond)
   109  
   110  		nBytes, err = cpty.Read(bytes)
   111  		cpty.Close()
   112  		readErr <- err
   113  	}()
   114  
   115  	select {
   116  	case err := <-readErr:
   117  		assert.NilError(c, err)
   118  	case <-time.After(2 * time.Second):
   119  		c.Fatal("timeout waiting for attach read")
   120  	}
   121  
   122  	assert.Assert(c, strings.Contains(string(bytes[:nBytes]), "/ #"))
   123  }
   124  
   125  // TestAttachDetach checks that attach in tty mode can be detached using the long container ID
   126  func (s *DockerCLIAttachSuite) TestAttachDetach(c *testing.T) {
   127  	out, _ := dockerCmd(c, "run", "-itd", "busybox", "cat")
   128  	id := strings.TrimSpace(out)
   129  	assert.NilError(c, waitRun(id))
   130  
   131  	cpty, tty, err := pty.Open()
   132  	assert.NilError(c, err)
   133  	defer cpty.Close()
   134  
   135  	cmd := exec.Command(dockerBinary, "attach", id)
   136  	cmd.Stdin = tty
   137  	stdout, err := cmd.StdoutPipe()
   138  	assert.NilError(c, err)
   139  	defer stdout.Close()
   140  	err = cmd.Start()
   141  	assert.NilError(c, err)
   142  	assert.NilError(c, waitRun(id))
   143  
   144  	_, err = cpty.Write([]byte("hello\n"))
   145  	assert.NilError(c, err)
   146  	out, err = bufio.NewReader(stdout).ReadString('\n')
   147  	assert.NilError(c, err)
   148  	assert.Equal(c, strings.TrimSpace(out), "hello")
   149  
   150  	// escape sequence
   151  	_, err = cpty.Write([]byte{16})
   152  	assert.NilError(c, err)
   153  	time.Sleep(100 * time.Millisecond)
   154  	_, err = cpty.Write([]byte{17})
   155  	assert.NilError(c, err)
   156  
   157  	ch := make(chan struct{})
   158  	go func() {
   159  		cmd.Wait()
   160  		close(ch)
   161  	}()
   162  
   163  	select {
   164  	case <-ch:
   165  	case <-time.After(1 * time.Second):
   166  		c.Fatal("timed out waiting for container to exit")
   167  	}
   168  
   169  	running := inspectField(c, id, "State.Running")
   170  	assert.Equal(c, running, "true") // container should be running
   171  }