github.com/oyvindsk/docker@v1.5.0/integration-cli/docker_cli_exec_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"reflect"
     9  	"sort"
    10  	"strings"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  func TestExec(t *testing.T) {
    17  	runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && sleep 100")
    18  	if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil {
    19  		t.Fatal(out, err)
    20  	}
    21  
    22  	execCmd := exec.Command(dockerBinary, "exec", "testing", "cat", "/tmp/file")
    23  	out, _, err := runCommandWithOutput(execCmd)
    24  	if err != nil {
    25  		t.Fatal(out, err)
    26  	}
    27  
    28  	out = strings.Trim(out, "\r\n")
    29  
    30  	if expected := "test"; out != expected {
    31  		t.Errorf("container exec should've printed %q but printed %q", expected, out)
    32  	}
    33  
    34  	deleteAllContainers()
    35  
    36  	logDone("exec - basic test")
    37  }
    38  
    39  func TestExecInteractiveStdinClose(t *testing.T) {
    40  	defer deleteAllContainers()
    41  	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "busybox", "/bin/cat"))
    42  	if err != nil {
    43  		t.Fatal(err)
    44  	}
    45  
    46  	contId := strings.TrimSpace(out)
    47  
    48  	returnchan := make(chan struct{})
    49  
    50  	go func() {
    51  		var err error
    52  		cmd := exec.Command(dockerBinary, "exec", "-i", contId, "/bin/ls", "/")
    53  		cmd.Stdin = os.Stdin
    54  		if err != nil {
    55  			t.Fatal(err)
    56  		}
    57  
    58  		out, err := cmd.CombinedOutput()
    59  		if err != nil {
    60  			t.Fatal(err, out)
    61  		}
    62  
    63  		if string(out) == "" {
    64  			t.Fatalf("Output was empty, likely blocked by standard input")
    65  		}
    66  
    67  		returnchan <- struct{}{}
    68  	}()
    69  
    70  	select {
    71  	case <-returnchan:
    72  	case <-time.After(10 * time.Second):
    73  		t.Fatal("timed out running docker exec")
    74  	}
    75  
    76  	logDone("exec - interactive mode closes stdin after execution")
    77  }
    78  
    79  func TestExecInteractive(t *testing.T) {
    80  	runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && sleep 100")
    81  	if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil {
    82  		t.Fatal(out, err)
    83  	}
    84  
    85  	execCmd := exec.Command(dockerBinary, "exec", "-i", "testing", "sh")
    86  	stdin, err := execCmd.StdinPipe()
    87  	if err != nil {
    88  		t.Fatal(err)
    89  	}
    90  	stdout, err := execCmd.StdoutPipe()
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	if err := execCmd.Start(); err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	if _, err := stdin.Write([]byte("cat /tmp/file\n")); err != nil {
    99  		t.Fatal(err)
   100  	}
   101  
   102  	r := bufio.NewReader(stdout)
   103  	line, err := r.ReadString('\n')
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  	line = strings.TrimSpace(line)
   108  	if line != "test" {
   109  		t.Fatalf("Output should be 'test', got '%q'", line)
   110  	}
   111  	if err := stdin.Close(); err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	finish := make(chan struct{})
   115  	go func() {
   116  		if err := execCmd.Wait(); err != nil {
   117  			t.Fatal(err)
   118  		}
   119  		close(finish)
   120  	}()
   121  	select {
   122  	case <-finish:
   123  	case <-time.After(1 * time.Second):
   124  		t.Fatal("docker exec failed to exit on stdin close")
   125  	}
   126  
   127  	deleteAllContainers()
   128  
   129  	logDone("exec - Interactive test")
   130  }
   131  
   132  func TestExecAfterContainerRestart(t *testing.T) {
   133  	runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top")
   134  	out, _, err := runCommandWithOutput(runCmd)
   135  	if err != nil {
   136  		t.Fatal(out, err)
   137  	}
   138  
   139  	cleanedContainerID := stripTrailingCharacters(out)
   140  
   141  	runCmd = exec.Command(dockerBinary, "restart", cleanedContainerID)
   142  	if out, _, err = runCommandWithOutput(runCmd); err != nil {
   143  		t.Fatal(out, err)
   144  	}
   145  
   146  	runCmd = exec.Command(dockerBinary, "exec", cleanedContainerID, "echo", "hello")
   147  	out, _, err = runCommandWithOutput(runCmd)
   148  	if err != nil {
   149  		t.Fatal(out, err)
   150  	}
   151  
   152  	outStr := strings.TrimSpace(out)
   153  	if outStr != "hello" {
   154  		t.Errorf("container should've printed hello, instead printed %q", outStr)
   155  	}
   156  
   157  	deleteAllContainers()
   158  
   159  	logDone("exec - exec running container after container restart")
   160  }
   161  
   162  func TestExecAfterDaemonRestart(t *testing.T) {
   163  	d := NewDaemon(t)
   164  	if err := d.StartWithBusybox(); err != nil {
   165  		t.Fatalf("Could not start daemon with busybox: %v", err)
   166  	}
   167  	defer d.Stop()
   168  
   169  	if out, err := d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil {
   170  		t.Fatalf("Could not run top: err=%v\n%s", err, out)
   171  	}
   172  
   173  	if err := d.Restart(); err != nil {
   174  		t.Fatalf("Could not restart daemon: %v", err)
   175  	}
   176  
   177  	if out, err := d.Cmd("start", "top"); err != nil {
   178  		t.Fatalf("Could not start top after daemon restart: err=%v\n%s", err, out)
   179  	}
   180  
   181  	out, err := d.Cmd("exec", "top", "echo", "hello")
   182  	if err != nil {
   183  		t.Fatalf("Could not exec on container top: err=%v\n%s", err, out)
   184  	}
   185  
   186  	outStr := strings.TrimSpace(string(out))
   187  	if outStr != "hello" {
   188  		t.Errorf("container should've printed hello, instead printed %q", outStr)
   189  	}
   190  
   191  	logDone("exec - exec running container after daemon restart")
   192  }
   193  
   194  // Regresssion test for #9155, #9044
   195  func TestExecEnv(t *testing.T) {
   196  	defer deleteAllContainers()
   197  
   198  	runCmd := exec.Command(dockerBinary, "run",
   199  		"-e", "LALA=value1",
   200  		"-e", "LALA=value2",
   201  		"-d", "--name", "testing", "busybox", "top")
   202  	if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil {
   203  		t.Fatal(out, err)
   204  	}
   205  
   206  	execCmd := exec.Command(dockerBinary, "exec", "testing", "env")
   207  	out, _, err := runCommandWithOutput(execCmd)
   208  	if err != nil {
   209  		t.Fatal(out, err)
   210  	}
   211  
   212  	if strings.Contains(out, "LALA=value1") ||
   213  		!strings.Contains(out, "LALA=value2") ||
   214  		!strings.Contains(out, "HOME=/root") {
   215  		t.Errorf("exec env(%q), expect %q, %q", out, "LALA=value2", "HOME=/root")
   216  	}
   217  
   218  	logDone("exec - exec inherits correct env")
   219  }
   220  
   221  func TestExecExitStatus(t *testing.T) {
   222  	runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "top", "busybox", "top")
   223  	if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil {
   224  		t.Fatal(out, err)
   225  	}
   226  
   227  	// Test normal (non-detached) case first
   228  	cmd := exec.Command(dockerBinary, "exec", "top", "sh", "-c", "exit 23")
   229  	ec, _ := runCommand(cmd)
   230  
   231  	if ec != 23 {
   232  		t.Fatalf("Should have had an ExitCode of 23, not: %d", ec)
   233  	}
   234  
   235  	logDone("exec - exec non-zero ExitStatus")
   236  }
   237  
   238  func TestExecPausedContainer(t *testing.T) {
   239  
   240  	defer deleteAllContainers()
   241  	defer unpauseAllContainers()
   242  
   243  	runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "top")
   244  	out, _, err := runCommandWithOutput(runCmd)
   245  	if err != nil {
   246  		t.Fatal(out, err)
   247  	}
   248  
   249  	ContainerID := stripTrailingCharacters(out)
   250  
   251  	pausedCmd := exec.Command(dockerBinary, "pause", "testing")
   252  	out, _, _, err = runCommandWithStdoutStderr(pausedCmd)
   253  	if err != nil {
   254  		t.Fatal(out, err)
   255  	}
   256  
   257  	execCmd := exec.Command(dockerBinary, "exec", "-i", "-t", ContainerID, "echo", "hello")
   258  	out, _, err = runCommandWithOutput(execCmd)
   259  	if err == nil {
   260  		t.Fatal("container should fail to exec new command if it is paused")
   261  	}
   262  
   263  	expected := ContainerID + " is paused, unpause the container before exec"
   264  	if !strings.Contains(out, expected) {
   265  		t.Fatal("container should not exec new command if it is paused")
   266  	}
   267  
   268  	logDone("exec - exec should not exec a pause container")
   269  }
   270  
   271  // regression test for #9476
   272  func TestExecTtyCloseStdin(t *testing.T) {
   273  	defer deleteAllContainers()
   274  
   275  	cmd := exec.Command(dockerBinary, "run", "-d", "-it", "--name", "exec_tty_stdin", "busybox")
   276  	if out, _, err := runCommandWithOutput(cmd); err != nil {
   277  		t.Fatal(out, err)
   278  	}
   279  
   280  	cmd = exec.Command(dockerBinary, "exec", "-i", "exec_tty_stdin", "cat")
   281  	stdinRw, err := cmd.StdinPipe()
   282  	if err != nil {
   283  		t.Fatal(err)
   284  	}
   285  
   286  	stdinRw.Write([]byte("test"))
   287  	stdinRw.Close()
   288  
   289  	if out, _, err := runCommandWithOutput(cmd); err != nil {
   290  		t.Fatal(out, err)
   291  	}
   292  
   293  	cmd = exec.Command(dockerBinary, "top", "exec_tty_stdin")
   294  	out, _, err := runCommandWithOutput(cmd)
   295  	if err != nil {
   296  		t.Fatal(out, err)
   297  	}
   298  
   299  	outArr := strings.Split(out, "\n")
   300  	if len(outArr) > 3 || strings.Contains(out, "nsenter-exec") {
   301  		// This is the really bad part
   302  		if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "rm", "-f", "exec_tty_stdin")); err != nil {
   303  			t.Fatal(out, err)
   304  		}
   305  
   306  		t.Fatalf("exec process left running\n\t %s", out)
   307  	}
   308  
   309  	logDone("exec - stdin is closed properly with tty enabled")
   310  }
   311  
   312  func TestExecTtyWithoutStdin(t *testing.T) {
   313  	defer deleteAllContainers()
   314  
   315  	cmd := exec.Command(dockerBinary, "run", "-d", "-ti", "busybox")
   316  	out, _, err := runCommandWithOutput(cmd)
   317  	if err != nil {
   318  		t.Fatalf("failed to start container: %v (%v)", out, err)
   319  	}
   320  
   321  	id := strings.TrimSpace(out)
   322  	if err := waitRun(id); err != nil {
   323  		t.Fatal(err)
   324  	}
   325  
   326  	defer func() {
   327  		cmd := exec.Command(dockerBinary, "kill", id)
   328  		if out, _, err := runCommandWithOutput(cmd); err != nil {
   329  			t.Fatalf("failed to kill container: %v (%v)", out, err)
   330  		}
   331  	}()
   332  
   333  	done := make(chan struct{})
   334  	go func() {
   335  		defer close(done)
   336  
   337  		cmd := exec.Command(dockerBinary, "exec", "-ti", id, "true")
   338  		if _, err := cmd.StdinPipe(); err != nil {
   339  			t.Fatal(err)
   340  		}
   341  
   342  		expected := "cannot enable tty mode"
   343  		if out, _, err := runCommandWithOutput(cmd); err == nil {
   344  			t.Fatal("exec should have failed")
   345  		} else if !strings.Contains(out, expected) {
   346  			t.Fatalf("exec failed with error %q: expected %q", out, expected)
   347  		}
   348  	}()
   349  
   350  	select {
   351  	case <-done:
   352  	case <-time.After(3 * time.Second):
   353  		t.Fatal("exec is running but should have failed")
   354  	}
   355  
   356  	logDone("exec - forbid piped stdin to tty enabled container")
   357  }
   358  
   359  func TestExecParseError(t *testing.T) {
   360  	defer deleteAllContainers()
   361  
   362  	runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "top", "busybox", "top")
   363  	if out, _, err := runCommandWithOutput(runCmd); err != nil {
   364  		t.Fatal(out, err)
   365  	}
   366  
   367  	// Test normal (non-detached) case first
   368  	cmd := exec.Command(dockerBinary, "exec", "top")
   369  	if _, stderr, code, err := runCommandWithStdoutStderr(cmd); err == nil || !strings.Contains(stderr, "See '"+dockerBinary+" exec --help'") || code == 0 {
   370  		t.Fatalf("Should have thrown error & point to help: %s", stderr)
   371  	}
   372  	logDone("exec - error on parseExec should point to help")
   373  }
   374  
   375  func TestExecStopNotHanging(t *testing.T) {
   376  	defer deleteAllContainers()
   377  	if out, err := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "top").CombinedOutput(); err != nil {
   378  		t.Fatal(out, err)
   379  	}
   380  
   381  	if err := exec.Command(dockerBinary, "exec", "testing", "top").Start(); err != nil {
   382  		t.Fatal(err)
   383  	}
   384  
   385  	wait := make(chan struct{})
   386  	go func() {
   387  		if out, err := exec.Command(dockerBinary, "stop", "testing").CombinedOutput(); err != nil {
   388  			t.Fatal(out, err)
   389  		}
   390  		close(wait)
   391  	}()
   392  	select {
   393  	case <-time.After(3 * time.Second):
   394  		t.Fatal("Container stop timed out")
   395  	case <-wait:
   396  	}
   397  	logDone("exec - container with exec not hanging on stop")
   398  }
   399  
   400  func TestExecCgroup(t *testing.T) {
   401  	defer deleteAllContainers()
   402  	var cmd *exec.Cmd
   403  
   404  	cmd = exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "top")
   405  	_, err := runCommand(cmd)
   406  	if err != nil {
   407  		t.Fatal(err)
   408  	}
   409  
   410  	cmd = exec.Command(dockerBinary, "exec", "testing", "cat", "/proc/1/cgroup")
   411  	out, _, err := runCommandWithOutput(cmd)
   412  	if err != nil {
   413  		t.Fatal(out, err)
   414  	}
   415  	containerCgroups := sort.StringSlice(strings.Split(string(out), "\n"))
   416  
   417  	var wg sync.WaitGroup
   418  	var s sync.Mutex
   419  	execCgroups := []sort.StringSlice{}
   420  	// exec a few times concurrently to get consistent failure
   421  	for i := 0; i < 5; i++ {
   422  		wg.Add(1)
   423  		go func() {
   424  			cmd = exec.Command(dockerBinary, "exec", "testing", "cat", "/proc/self/cgroup")
   425  			out, _, err := runCommandWithOutput(cmd)
   426  			if err != nil {
   427  				t.Fatal(out, err)
   428  			}
   429  			cg := sort.StringSlice(strings.Split(string(out), "\n"))
   430  
   431  			s.Lock()
   432  			execCgroups = append(execCgroups, cg)
   433  			s.Unlock()
   434  			wg.Done()
   435  		}()
   436  	}
   437  	wg.Wait()
   438  
   439  	for _, cg := range execCgroups {
   440  		if !reflect.DeepEqual(cg, containerCgroups) {
   441  			fmt.Println("exec cgroups:")
   442  			for _, name := range cg {
   443  				fmt.Printf(" %s\n", name)
   444  			}
   445  
   446  			fmt.Println("container cgroups:")
   447  			for _, name := range containerCgroups {
   448  				fmt.Printf(" %s\n", name)
   449  			}
   450  			t.Fatal("cgroups mismatched")
   451  		}
   452  	}
   453  
   454  	logDone("exec - exec has the container cgroups")
   455  }