github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/integration-cli/docker_api_exec_resize_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "strings" 10 "sync" 11 "testing" 12 13 "github.com/docker/docker/integration-cli/cli" 14 "github.com/docker/docker/testutil" 15 "github.com/docker/docker/testutil/request" 16 "github.com/pkg/errors" 17 "gotest.tools/v3/assert" 18 ) 19 20 func (s *DockerAPISuite) TestExecResizeAPIHeightWidthNoInt(c *testing.T) { 21 testRequires(c, DaemonIsLinux) 22 out := cli.DockerCmd(c, "run", "-d", "busybox", "top").Stdout() 23 cleanedContainerID := strings.TrimSpace(out) 24 25 endpoint := "/exec/" + cleanedContainerID + "/resize?h=foo&w=bar" 26 res, _, err := request.Post(testutil.GetContext(c), endpoint) 27 assert.NilError(c, err) 28 assert.Equal(c, res.StatusCode, http.StatusBadRequest) 29 } 30 31 // Part of #14845 32 func (s *DockerAPISuite) TestExecResizeImmediatelyAfterExecStart(c *testing.T) { 33 name := "exec_resize_test" 34 cli.DockerCmd(c, "run", "-d", "-i", "-t", "--name", name, "--restart", "always", "busybox", "/bin/sh") 35 36 testExecResize := func() error { 37 data := map[string]interface{}{ 38 "AttachStdin": true, 39 "Cmd": []string{"/bin/sh"}, 40 } 41 uri := fmt.Sprintf("/containers/%s/exec", name) 42 res, body, err := request.Post(testutil.GetContext(c), uri, request.JSONBody(data)) 43 if err != nil { 44 return err 45 } 46 if res.StatusCode != http.StatusCreated { 47 return errors.Errorf("POST %s is expected to return %d, got %d", uri, http.StatusCreated, res.StatusCode) 48 } 49 50 buf, err := request.ReadBody(body) 51 assert.NilError(c, err) 52 53 out := map[string]string{} 54 err = json.Unmarshal(buf, &out) 55 if err != nil { 56 return errors.Wrap(err, "ExecCreate returned invalid json") 57 } 58 59 execID := out["Id"] 60 if len(execID) < 1 { 61 return errors.New("ExecCreate got invalid execID") 62 } 63 64 payload := bytes.NewBufferString(`{"Tty":true}`) 65 wc, _, err := requestHijack(http.MethodPost, fmt.Sprintf("/exec/%s/start", execID), payload, "application/json", request.DaemonHost()) 66 if err != nil { 67 return errors.Wrap(err, "failed to start the exec") 68 } 69 defer wc.Close() 70 71 _, rc, err := request.Post(testutil.GetContext(c), fmt.Sprintf("/exec/%s/resize?h=24&w=80", execID), request.ContentType("text/plain")) 72 if err != nil { 73 // It's probably a panic of the daemon if io.ErrUnexpectedEOF is returned. 74 if err == io.ErrUnexpectedEOF { 75 return errors.New("the daemon might have crashed") 76 } 77 // Other error happened, should be reported. 78 return errors.Wrap(err, "failed to exec resize immediately after start") 79 } 80 81 rc.Close() 82 83 return nil 84 } 85 86 // The panic happens when daemon.ContainerExecStart is called but the 87 // container.Exec is not called. 88 // Because the panic is not 100% reproducible, we send the requests concurrently 89 // to increase the probability that the problem is triggered. 90 var ( 91 n = 10 92 ch = make(chan error, n) 93 wg sync.WaitGroup 94 ) 95 for i := 0; i < n; i++ { 96 wg.Add(1) 97 go func() { 98 defer wg.Done() 99 if err := testExecResize(); err != nil { 100 ch <- err 101 } 102 }() 103 } 104 105 wg.Wait() 106 select { 107 case err := <-ch: 108 c.Fatal(err.Error()) 109 default: 110 } 111 }