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 }