github.com/liuzhiyi/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 }