| package container // import "github.com/docker/docker/integration/container" | |
| import ( | |
| "context" | |
| "io" | |
| "testing" | |
| "time" | |
| "github.com/docker/docker/api/types" | |
| "github.com/docker/docker/api/types/strslice" | |
| "github.com/docker/docker/api/types/versions" | |
| "github.com/docker/docker/integration/internal/container" | |
| "gotest.tools/v3/assert" | |
| is "gotest.tools/v3/assert/cmp" | |
| "gotest.tools/v3/skip" | |
| ) | |
| // TestExecWithCloseStdin adds case for moby#37870 issue. | |
| func TestExecWithCloseStdin(t *testing.T) { | |
| skip.If(t, testEnv.RuntimeIsWindowsContainerd(), "FIXME. Hang on Windows + containerd combination") | |
| skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "broken in earlier versions") | |
| defer setupTest(t)() | |
| ctx := context.Background() | |
| client := testEnv.APIClient() | |
| // run top with detached mode | |
| cID := container.Run(ctx, t, client) | |
| expected := "closeIO" | |
| execResp, err := client.ContainerExecCreate(ctx, cID, | |
| types.ExecConfig{ | |
| AttachStdin: true, | |
| AttachStdout: true, | |
| Cmd: strslice.StrSlice([]string{"sh", "-c", "cat && echo " + expected}), | |
| }, | |
| ) | |
| assert.NilError(t, err) | |
| resp, err := client.ContainerExecAttach(ctx, execResp.ID, | |
| types.ExecStartCheck{ | |
| Detach: false, | |
| Tty: false, | |
| }, | |
| ) | |
| assert.NilError(t, err) | |
| defer resp.Close() | |
| // close stdin to send EOF to cat | |
| assert.NilError(t, resp.CloseWrite()) | |
| var ( | |
| waitCh = make(chan struct{}) | |
| resCh = make(chan struct { | |
| content string | |
| err error | |
| }, 1) | |
| ) | |
| go func() { | |
| close(waitCh) | |
| defer close(resCh) | |
| r, err := io.ReadAll(resp.Reader) | |
| resCh <- struct { | |
| content string | |
| err error | |
| }{ | |
| content: string(r), | |
| err: err, | |
| } | |
| }() | |
| <-waitCh | |
| select { | |
| case <-time.After(3 * time.Second): | |
| t.Fatal("failed to read the content in time") | |
| case got := <-resCh: | |
| assert.NilError(t, got.err) | |
| // NOTE: using Contains because no-tty's stream contains UX information | |
| // like size, stream type. | |
| assert.Assert(t, is.Contains(got.content, expected)) | |
| } | |
| } | |
| func TestExec(t *testing.T) { | |
| skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.35"), "broken in earlier versions") | |
| defer setupTest(t)() | |
| ctx := context.Background() | |
| client := testEnv.APIClient() | |
| cID := container.Run(ctx, t, client, container.WithTty(true), container.WithWorkingDir("/root")) | |
| id, err := client.ContainerExecCreate(ctx, cID, | |
| types.ExecConfig{ | |
| WorkingDir: "/tmp", | |
| Env: strslice.StrSlice([]string{"FOO=BAR"}), | |
| AttachStdout: true, | |
| Cmd: strslice.StrSlice([]string{"sh", "-c", "env"}), | |
| }, | |
| ) | |
| assert.NilError(t, err) | |
| inspect, err := client.ContainerExecInspect(ctx, id.ID) | |
| assert.NilError(t, err) | |
| assert.Check(t, is.Equal(inspect.ExecID, id.ID)) | |
| resp, err := client.ContainerExecAttach(ctx, id.ID, | |
| types.ExecStartCheck{ | |
| Detach: false, | |
| Tty: false, | |
| }, | |
| ) | |
| assert.NilError(t, err) | |
| defer resp.Close() | |
| r, err := io.ReadAll(resp.Reader) | |
| assert.NilError(t, err) | |
| out := string(r) | |
| assert.NilError(t, err) | |
| expected := "PWD=/tmp" | |
| if testEnv.OSType == "windows" { | |
| expected = "PWD=C:/tmp" | |
| } | |
| assert.Assert(t, is.Contains(out, expected), "exec command not running in expected /tmp working directory") | |
| assert.Assert(t, is.Contains(out, "FOO=BAR"), "exec command not running with expected environment variable FOO") | |
| } | |
| func TestExecUser(t *testing.T) { | |
| skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "broken in earlier versions") | |
| skip.If(t, testEnv.OSType == "windows", "FIXME. Probably needs to wait for container to be in running state.") | |
| defer setupTest(t)() | |
| ctx := context.Background() | |
| client := testEnv.APIClient() | |
| cID := container.Run(ctx, t, client, container.WithTty(true), container.WithUser("1:1")) | |
| result, err := container.Exec(ctx, client, cID, []string{"id"}) | |
| assert.NilError(t, err) | |
| assert.Assert(t, is.Contains(result.Stdout(), "uid=1(daemon) gid=1(daemon)"), "exec command not running as uid/gid 1") | |
| } |