diff --git a/integration-cli/docker_cli_attach_test.go b/integration-cli/docker_cli_attach_test.go index 606480c875..c9568229e6 100644 --- a/integration-cli/docker_cli_attach_test.go +++ b/integration-cli/docker_cli_attach_test.go @@ -1,6 +1,7 @@ package main import ( + "io" "os/exec" "strings" "sync" @@ -8,46 +9,81 @@ import ( "time" ) +const attachWait = 5 * time.Second + func TestMultipleAttachRestart(t *testing.T) { - cmd := exec.Command(dockerBinary, "run", "--name", "attacher", "-d", "busybox", - "/bin/sh", "-c", "sleep 2 && echo hello") + defer deleteAllContainers() - group := sync.WaitGroup{} - group.Add(4) + endGroup := &sync.WaitGroup{} + startGroup := &sync.WaitGroup{} + endGroup.Add(3) + startGroup.Add(3) - defer func() { - cmd = exec.Command(dockerBinary, "kill", "attacher") - if _, err := runCommand(cmd); err != nil { - t.Fatal(err) - } - deleteAllContainers() + if err := waitForContainer("attacher", "-d", "busybox", "/bin/sh", "-c", "while true; do sleep 1; echo hello; done"); err != nil { + t.Fatal(err) + } + + startDone := make(chan struct{}) + endDone := make(chan struct{}) + + go func() { + endGroup.Wait() + close(endDone) }() go func() { - defer group.Done() - out, _, err := runCommandWithOutput(cmd) - if err != nil { - t.Fatal(err, out) - } + startGroup.Wait() + close(startDone) }() - time.Sleep(500 * time.Millisecond) for i := 0; i < 3; i++ { go func() { - defer group.Done() c := exec.Command(dockerBinary, "attach", "attacher") - out, _, err := runCommandWithOutput(c) + defer func() { + c.Wait() + endGroup.Done() + }() + + out, err := c.StdoutPipe() if err != nil { - t.Fatal(err, out) + t.Fatal(err) } - if actual := strings.Trim(out, "\r\n"); actual != "hello" { - t.Fatalf("unexpected output %s expected hello", actual) + + if _, err := startCommand(c); err != nil { + t.Fatal(err) + } + + buf := make([]byte, 1024) + + if _, err := out.Read(buf); err != nil && err != io.EOF { + t.Fatal(err) + } + + startGroup.Done() + + if !strings.Contains(string(buf), "hello") { + t.Fatalf("unexpected output %s expected hello\n", string(buf)) } }() } - group.Wait() + select { + case <-startDone: + case <-time.After(attachWait): + t.Fatalf("Attaches did not initialize properly") + } + + cmd := exec.Command(dockerBinary, "kill", "attacher") + if _, err := runCommand(cmd); err != nil { + t.Fatal(err) + } + + select { + case <-endDone: + case <-time.After(attachWait): + t.Fatalf("Attaches did not finish properly") + } logDone("attach - multiple attach") } diff --git a/integration-cli/utils.go b/integration-cli/utils.go index c414c9a2d9..b01fdd85fa 100644 --- a/integration-cli/utils.go +++ b/integration-cli/utils.go @@ -10,6 +10,7 @@ import ( "strings" "syscall" "testing" + "time" ) func getExitCode(err error) (int, error) { @@ -134,3 +135,43 @@ func convertSliceOfStringsToMap(input []string) map[string]struct{} { } return output } + +func waitForContainer(contId string, args ...string) error { + args = append([]string{"run", "--name", contId}, args...) + cmd := exec.Command(dockerBinary, args...) + if _, err := runCommand(cmd); err != nil { + return err + } + + if err := waitRun(contId); err != nil { + return err + } + + return nil +} + +func waitRun(contId string) error { + after := time.After(5 * time.Second) + + for { + cmd := exec.Command(dockerBinary, "inspect", "-f", "{{.State.Running}}", contId) + out, _, err := runCommandWithOutput(cmd) + if err != nil { + return fmt.Errorf("error executing docker inspect: %v", err) + } + + if strings.Contains(out, "true") { + break + } + + select { + case <-after: + return fmt.Errorf("container did not come up in time") + default: + } + + time.Sleep(100 * time.Millisecond) + } + + return nil +}