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