github.com/kaisenlinux/docker@v0.0.0-20230510090727-ea55db55fac7/engine/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 *DockerSuite) 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 62 func (s *DockerSuite) TestAttachAfterDetach(c *testing.T) { 63 name := "detachtest" 64 65 cpty, tty, err := pty.Open() 66 assert.NilError(c, err, "Could not open pty: %v", err) 67 cmd := exec.Command(dockerBinary, "run", "-ti", "--name", name, "busybox") 68 cmd.Stdin = tty 69 cmd.Stdout = tty 70 cmd.Stderr = tty 71 72 cmdExit := make(chan error, 1) 73 go func() { 74 cmdExit <- cmd.Run() 75 close(cmdExit) 76 }() 77 78 assert.Assert(c, waitRun(name) == nil) 79 80 cpty.Write([]byte{16}) 81 time.Sleep(100 * time.Millisecond) 82 cpty.Write([]byte{17}) 83 84 select { 85 case <-cmdExit: 86 case <-time.After(5 * time.Second): 87 c.Fatal("timeout while detaching") 88 } 89 90 cpty, tty, err = pty.Open() 91 assert.NilError(c, err, "Could not open pty: %v", err) 92 93 cmd = exec.Command(dockerBinary, "attach", name) 94 cmd.Stdin = tty 95 cmd.Stdout = tty 96 cmd.Stderr = tty 97 98 err = cmd.Start() 99 assert.NilError(c, err) 100 defer cmd.Process.Kill() 101 102 bytes := make([]byte, 10) 103 var nBytes int 104 readErr := make(chan error, 1) 105 106 go func() { 107 time.Sleep(500 * time.Millisecond) 108 cpty.Write([]byte("\n")) 109 time.Sleep(500 * time.Millisecond) 110 111 nBytes, err = cpty.Read(bytes) 112 cpty.Close() 113 readErr <- err 114 }() 115 116 select { 117 case err := <-readErr: 118 assert.NilError(c, err) 119 case <-time.After(2 * time.Second): 120 c.Fatal("timeout waiting for attach read") 121 } 122 123 assert.Assert(c, strings.Contains(string(bytes[:nBytes]), "/ #")) 124 } 125 126 // TestAttachDetach checks that attach in tty mode can be detached using the long container ID 127 func (s *DockerSuite) TestAttachDetach(c *testing.T) { 128 out, _ := dockerCmd(c, "run", "-itd", "busybox", "cat") 129 id := strings.TrimSpace(out) 130 assert.NilError(c, waitRun(id)) 131 132 cpty, tty, err := pty.Open() 133 assert.NilError(c, err) 134 defer cpty.Close() 135 136 cmd := exec.Command(dockerBinary, "attach", id) 137 cmd.Stdin = tty 138 stdout, err := cmd.StdoutPipe() 139 assert.NilError(c, err) 140 defer stdout.Close() 141 err = cmd.Start() 142 assert.NilError(c, err) 143 assert.NilError(c, waitRun(id)) 144 145 _, err = cpty.Write([]byte("hello\n")) 146 assert.NilError(c, err) 147 out, err = bufio.NewReader(stdout).ReadString('\n') 148 assert.NilError(c, err) 149 assert.Equal(c, strings.TrimSpace(out), "hello") 150 151 // escape sequence 152 _, err = cpty.Write([]byte{16}) 153 assert.NilError(c, err) 154 time.Sleep(100 * time.Millisecond) 155 _, err = cpty.Write([]byte{17}) 156 assert.NilError(c, err) 157 158 ch := make(chan struct{}) 159 go func() { 160 cmd.Wait() 161 close(ch) 162 }() 163 164 select { 165 case <-ch: 166 case <-time.After(1 * time.Second): 167 c.Fatal("timed out waiting for container to exit") 168 } 169 170 running := inspectField(c, id, "State.Running") 171 assert.Equal(c, running, "true") // container should be running 172 }