github.com/moby/docker@v26.1.3+incompatible/integration/container/exec_test.go (about)

     1  package container // import "github.com/docker/docker/integration/container"
     2  
     3  import (
     4  	"io"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/docker/docker/api/types"
    10  	"github.com/docker/docker/api/types/strslice"
    11  	"github.com/docker/docker/integration/internal/container"
    12  	"gotest.tools/v3/assert"
    13  	is "gotest.tools/v3/assert/cmp"
    14  	"gotest.tools/v3/skip"
    15  )
    16  
    17  // TestExecWithCloseStdin adds case for moby#37870 issue.
    18  func TestExecWithCloseStdin(t *testing.T) {
    19  	skip.If(t, testEnv.RuntimeIsWindowsContainerd(), "FIXME. Hang on Windows + containerd combination")
    20  	ctx := setupTest(t)
    21  
    22  	apiClient := testEnv.APIClient()
    23  
    24  	// run top with detached mode
    25  	cID := container.Run(ctx, t, apiClient)
    26  
    27  	expected := "closeIO"
    28  	execResp, err := apiClient.ContainerExecCreate(ctx, cID,
    29  		types.ExecConfig{
    30  			AttachStdin:  true,
    31  			AttachStdout: true,
    32  			Cmd:          strslice.StrSlice([]string{"sh", "-c", "cat && echo " + expected}),
    33  		},
    34  	)
    35  	assert.NilError(t, err)
    36  
    37  	resp, err := apiClient.ContainerExecAttach(ctx, execResp.ID,
    38  		types.ExecStartCheck{
    39  			Detach: false,
    40  			Tty:    false,
    41  		},
    42  	)
    43  	assert.NilError(t, err)
    44  	defer resp.Close()
    45  
    46  	// close stdin to send EOF to cat
    47  	assert.NilError(t, resp.CloseWrite())
    48  
    49  	var (
    50  		waitCh = make(chan struct{})
    51  		resCh  = make(chan struct {
    52  			content string
    53  			err     error
    54  		}, 1)
    55  	)
    56  
    57  	go func() {
    58  		close(waitCh)
    59  		defer close(resCh)
    60  		r, err := io.ReadAll(resp.Reader)
    61  
    62  		resCh <- struct {
    63  			content string
    64  			err     error
    65  		}{
    66  			content: string(r),
    67  			err:     err,
    68  		}
    69  	}()
    70  
    71  	<-waitCh
    72  	select {
    73  	case <-time.After(3 * time.Second):
    74  		t.Fatal("failed to read the content in time")
    75  	case got := <-resCh:
    76  		assert.NilError(t, got.err)
    77  
    78  		// NOTE: using Contains because no-tty's stream contains UX information
    79  		// like size, stream type.
    80  		assert.Assert(t, is.Contains(got.content, expected))
    81  	}
    82  }
    83  
    84  func TestExec(t *testing.T) {
    85  	ctx := setupTest(t)
    86  	apiClient := testEnv.APIClient()
    87  
    88  	cID := container.Run(ctx, t, apiClient, container.WithTty(true), container.WithWorkingDir("/root"))
    89  
    90  	id, err := apiClient.ContainerExecCreate(ctx, cID,
    91  		types.ExecConfig{
    92  			WorkingDir:   "/tmp",
    93  			Env:          strslice.StrSlice([]string{"FOO=BAR"}),
    94  			AttachStdout: true,
    95  			Cmd:          strslice.StrSlice([]string{"sh", "-c", "env"}),
    96  		},
    97  	)
    98  	assert.NilError(t, err)
    99  
   100  	inspect, err := apiClient.ContainerExecInspect(ctx, id.ID)
   101  	assert.NilError(t, err)
   102  	assert.Check(t, is.Equal(inspect.ExecID, id.ID))
   103  
   104  	resp, err := apiClient.ContainerExecAttach(ctx, id.ID,
   105  		types.ExecStartCheck{
   106  			Detach: false,
   107  			Tty:    false,
   108  		},
   109  	)
   110  	assert.NilError(t, err)
   111  	defer resp.Close()
   112  	r, err := io.ReadAll(resp.Reader)
   113  	assert.NilError(t, err)
   114  	out := string(r)
   115  	assert.NilError(t, err)
   116  	expected := "PWD=/tmp"
   117  	if testEnv.DaemonInfo.OSType == "windows" {
   118  		expected = "PWD=C:/tmp"
   119  	}
   120  	assert.Assert(t, is.Contains(out, expected), "exec command not running in expected /tmp working directory")
   121  	assert.Assert(t, is.Contains(out, "FOO=BAR"), "exec command not running with expected environment variable FOO")
   122  }
   123  
   124  func TestExecUser(t *testing.T) {
   125  	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME. Probably needs to wait for container to be in running state.")
   126  	ctx := setupTest(t)
   127  	apiClient := testEnv.APIClient()
   128  
   129  	cID := container.Run(ctx, t, apiClient, container.WithTty(true), container.WithUser("1:1"))
   130  
   131  	result, err := container.Exec(ctx, apiClient, cID, []string{"id"})
   132  	assert.NilError(t, err)
   133  
   134  	assert.Assert(t, is.Contains(result.Stdout(), "uid=1(daemon) gid=1(daemon)"), "exec command not running as uid/gid 1")
   135  }
   136  
   137  // Test that additional groups set with `--group-add` are kept on exec when the container
   138  // also has a user set.
   139  // (regression test for https://github.com/moby/moby/issues/46712)
   140  func TestExecWithGroupAdd(t *testing.T) {
   141  	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME. Probably needs to wait for container to be in running state.")
   142  
   143  	ctx := setupTest(t)
   144  	apiClient := testEnv.APIClient()
   145  
   146  	cID := container.Run(ctx, t, apiClient, container.WithTty(true), container.WithUser("root:root"), container.WithAdditionalGroups("staff", "wheel", "audio", "777"), container.WithCmd("sleep", "5"))
   147  
   148  	result, err := container.Exec(ctx, apiClient, cID, []string{"id"})
   149  	assert.NilError(t, err)
   150  
   151  	assert.Assert(t,
   152  		is.Equal(strings.TrimSpace(result.Stdout()), "uid=0(root) gid=0(root) groups=0(root),10(wheel),29(audio),50(staff),777"),
   153  		"exec command not keeping additional groups w/ user")
   154  }