github.com/janma/nomad@v0.11.3/drivers/docker/driver_unix_test.go (about) 1 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 2 3 package docker 4 5 import ( 6 "fmt" 7 "io" 8 "os" 9 "path/filepath" 10 "runtime" 11 "sort" 12 "strconv" 13 "strings" 14 "testing" 15 "time" 16 17 docker "github.com/fsouza/go-dockerclient" 18 "github.com/hashicorp/nomad/client/allocdir" 19 "github.com/hashicorp/nomad/client/testutil" 20 "github.com/hashicorp/nomad/helper/freeport" 21 "github.com/hashicorp/nomad/helper/uuid" 22 "github.com/hashicorp/nomad/plugins/drivers" 23 dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils" 24 tu "github.com/hashicorp/nomad/testutil" 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 ) 28 29 func TestDockerDriver_User(t *testing.T) { 30 if !tu.IsCI() { 31 t.Parallel() 32 } 33 testutil.DockerCompatible(t) 34 task, cfg, ports := dockerTask(t) 35 defer freeport.Return(ports) 36 task.User = "alice" 37 cfg.Command = "/bin/sleep" 38 cfg.Args = []string{"10000"} 39 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 40 41 d := dockerDriverHarness(t, nil) 42 cleanup := d.MkAllocDir(task, true) 43 defer cleanup() 44 copyImage(t, task.TaskDir(), "busybox.tar") 45 46 _, _, err := d.StartTask(task) 47 if err == nil { 48 d.DestroyTask(task.ID, true) 49 t.Fatalf("Should've failed") 50 } 51 52 if !strings.Contains(err.Error(), "alice") { 53 t.Fatalf("Expected failure string not found, found %q instead", err.Error()) 54 } 55 } 56 57 func TestDockerDriver_NetworkAliases_Bridge(t *testing.T) { 58 if !tu.IsCI() { 59 t.Parallel() 60 } 61 testutil.DockerCompatible(t) 62 require := require.New(t) 63 64 // Because go-dockerclient doesn't provide api for query network aliases, just check that 65 // a container can be created with a 'network_aliases' property 66 67 // Create network, network-scoped alias is supported only for containers in user defined networks 68 client := newTestDockerClient(t) 69 networkOpts := docker.CreateNetworkOptions{Name: "foobar", Driver: "bridge"} 70 network, err := client.CreateNetwork(networkOpts) 71 require.NoError(err) 72 defer client.RemoveNetwork(network.ID) 73 74 expected := []string{"foobar"} 75 taskCfg := newTaskConfig("", busyboxLongRunningCmd) 76 taskCfg.NetworkMode = network.Name 77 taskCfg.NetworkAliases = expected 78 task := &drivers.TaskConfig{ 79 ID: uuid.Generate(), 80 Name: "busybox", 81 Resources: basicResources, 82 } 83 require.NoError(task.EncodeConcreteDriverConfig(&taskCfg)) 84 85 d := dockerDriverHarness(t, nil) 86 cleanup := d.MkAllocDir(task, true) 87 defer cleanup() 88 copyImage(t, task.TaskDir(), "busybox.tar") 89 90 _, _, err = d.StartTask(task) 91 require.NoError(err) 92 require.NoError(d.WaitUntilStarted(task.ID, 5*time.Second)) 93 94 defer d.DestroyTask(task.ID, true) 95 96 dockerDriver, ok := d.Impl().(*Driver) 97 require.True(ok) 98 99 handle, ok := dockerDriver.tasks.Get(task.ID) 100 require.True(ok) 101 102 _, err = client.InspectContainer(handle.containerID) 103 require.NoError(err) 104 } 105 106 func TestDockerDriver_NetworkMode_Host(t *testing.T) { 107 if !tu.IsCI() { 108 t.Parallel() 109 } 110 testutil.DockerCompatible(t) 111 expected := "host" 112 113 taskCfg := newTaskConfig("", busyboxLongRunningCmd) 114 taskCfg.NetworkMode = expected 115 116 task := &drivers.TaskConfig{ 117 ID: uuid.Generate(), 118 Name: "busybox-demo", 119 Resources: basicResources, 120 } 121 require.NoError(t, task.EncodeConcreteDriverConfig(&taskCfg)) 122 123 d := dockerDriverHarness(t, nil) 124 cleanup := d.MkAllocDir(task, true) 125 defer cleanup() 126 copyImage(t, task.TaskDir(), "busybox.tar") 127 128 _, _, err := d.StartTask(task) 129 require.NoError(t, err) 130 131 require.NoError(t, d.WaitUntilStarted(task.ID, 5*time.Second)) 132 133 defer d.DestroyTask(task.ID, true) 134 135 dockerDriver, ok := d.Impl().(*Driver) 136 require.True(t, ok) 137 138 handle, ok := dockerDriver.tasks.Get(task.ID) 139 require.True(t, ok) 140 141 container, err := client.InspectContainer(handle.containerID) 142 if err != nil { 143 t.Fatalf("err: %v", err) 144 } 145 146 actual := container.HostConfig.NetworkMode 147 require.Equal(t, expected, actual) 148 } 149 150 func TestDockerDriver_CPUCFSPeriod(t *testing.T) { 151 if !tu.IsCI() { 152 t.Parallel() 153 } 154 testutil.DockerCompatible(t) 155 156 task, cfg, ports := dockerTask(t) 157 defer freeport.Return(ports) 158 cfg.CPUHardLimit = true 159 cfg.CPUCFSPeriod = 1000000 160 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 161 162 client, _, handle, cleanup := dockerSetup(t, task, nil) 163 defer cleanup() 164 165 waitForExist(t, client, handle.containerID) 166 167 container, err := client.InspectContainer(handle.containerID) 168 require.NoError(t, err) 169 170 require.Equal(t, cfg.CPUCFSPeriod, container.HostConfig.CPUPeriod) 171 } 172 173 func TestDockerDriver_Sysctl_Ulimit(t *testing.T) { 174 testutil.DockerCompatible(t) 175 task, cfg, ports := dockerTask(t) 176 defer freeport.Return(ports) 177 expectedUlimits := map[string]string{ 178 "nproc": "4242", 179 "nofile": "2048:4096", 180 } 181 cfg.Sysctl = map[string]string{ 182 "net.core.somaxconn": "16384", 183 } 184 cfg.Ulimit = expectedUlimits 185 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 186 187 client, d, handle, cleanup := dockerSetup(t, task, nil) 188 defer cleanup() 189 require.NoError(t, d.WaitUntilStarted(task.ID, 5*time.Second)) 190 191 container, err := client.InspectContainer(handle.containerID) 192 assert.Nil(t, err, "unexpected error: %v", err) 193 194 want := "16384" 195 got := container.HostConfig.Sysctls["net.core.somaxconn"] 196 assert.Equal(t, want, got, "Wrong net.core.somaxconn config for docker job. Expect: %s, got: %s", want, got) 197 198 expectedUlimitLen := 2 199 actualUlimitLen := len(container.HostConfig.Ulimits) 200 assert.Equal(t, want, got, "Wrong number of ulimit configs for docker job. Expect: %d, got: %d", expectedUlimitLen, actualUlimitLen) 201 202 for _, got := range container.HostConfig.Ulimits { 203 if expectedStr, ok := expectedUlimits[got.Name]; !ok { 204 t.Errorf("%s config unexpected for docker job.", got.Name) 205 } else { 206 if !strings.Contains(expectedStr, ":") { 207 expectedStr = expectedStr + ":" + expectedStr 208 } 209 210 splitted := strings.SplitN(expectedStr, ":", 2) 211 soft, _ := strconv.Atoi(splitted[0]) 212 hard, _ := strconv.Atoi(splitted[1]) 213 assert.Equal(t, int64(soft), got.Soft, "Wrong soft %s ulimit for docker job. Expect: %d, got: %d", got.Name, soft, got.Soft) 214 assert.Equal(t, int64(hard), got.Hard, "Wrong hard %s ulimit for docker job. Expect: %d, got: %d", got.Name, hard, got.Hard) 215 216 } 217 } 218 } 219 220 func TestDockerDriver_Sysctl_Ulimit_Errors(t *testing.T) { 221 testutil.DockerCompatible(t) 222 brokenConfigs := []map[string]string{ 223 { 224 "nofile": "", 225 }, 226 { 227 "nofile": "abc:1234", 228 }, 229 { 230 "nofile": "1234:abc", 231 }, 232 } 233 234 testCases := []struct { 235 ulimitConfig map[string]string 236 err error 237 }{ 238 {brokenConfigs[0], fmt.Errorf("Malformed ulimit specification nofile: \"\", cannot be empty")}, 239 {brokenConfigs[1], fmt.Errorf("Malformed soft ulimit nofile: abc:1234")}, 240 {brokenConfigs[2], fmt.Errorf("Malformed hard ulimit nofile: 1234:abc")}, 241 } 242 243 for _, tc := range testCases { 244 task, cfg, ports := dockerTask(t) 245 cfg.Ulimit = tc.ulimitConfig 246 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 247 248 d := dockerDriverHarness(t, nil) 249 cleanup := d.MkAllocDir(task, true) 250 defer cleanup() 251 copyImage(t, task.TaskDir(), "busybox.tar") 252 253 _, _, err := d.StartTask(task) 254 require.NotNil(t, err, "Expected non nil error") 255 require.Contains(t, err.Error(), tc.err.Error()) 256 freeport.Return(ports) 257 } 258 } 259 260 // This test does not run on Windows due to stricter path validation in the 261 // negative case for non existent mount paths. We should write a similar test 262 // for windows. 263 func TestDockerDriver_BindMountsHonorVolumesEnabledFlag(t *testing.T) { 264 t.Parallel() 265 266 testutil.DockerCompatible(t) 267 268 allocDir := "/tmp/nomad/alloc-dir" 269 270 cases := []struct { 271 name string 272 requiresVolumes bool 273 274 volumeDriver string 275 volumes []string 276 277 expectedVolumes []string 278 }{ 279 { 280 name: "basic plugin", 281 requiresVolumes: true, 282 volumeDriver: "nfs", 283 volumes: []string{"test-path:/tmp/taskpath"}, 284 expectedVolumes: []string{"test-path:/tmp/taskpath"}, 285 }, 286 { 287 name: "absolute default driver", 288 requiresVolumes: true, 289 volumeDriver: "", 290 volumes: []string{"/abs/test-path:/tmp/taskpath"}, 291 expectedVolumes: []string{"/abs/test-path:/tmp/taskpath"}, 292 }, 293 { 294 name: "absolute local driver", 295 requiresVolumes: true, 296 volumeDriver: "local", 297 volumes: []string{"/abs/test-path:/tmp/taskpath"}, 298 expectedVolumes: []string{"/abs/test-path:/tmp/taskpath"}, 299 }, 300 { 301 name: "relative default driver", 302 requiresVolumes: false, 303 volumeDriver: "", 304 volumes: []string{"test-path:/tmp/taskpath"}, 305 expectedVolumes: []string{"/tmp/nomad/alloc-dir/demo/test-path:/tmp/taskpath"}, 306 }, 307 { 308 name: "named volume local driver", 309 requiresVolumes: true, 310 volumeDriver: "local", 311 volumes: []string{"test-path:/tmp/taskpath"}, 312 expectedVolumes: []string{"test-path:/tmp/taskpath"}, 313 }, 314 { 315 name: "relative outside task-dir default driver", 316 requiresVolumes: false, 317 volumeDriver: "", 318 volumes: []string{"../test-path:/tmp/taskpath"}, 319 expectedVolumes: []string{"/tmp/nomad/alloc-dir/test-path:/tmp/taskpath"}, 320 }, 321 { 322 name: "relative outside alloc-dir default driver", 323 requiresVolumes: true, 324 volumeDriver: "", 325 volumes: []string{"../../test-path:/tmp/taskpath"}, 326 expectedVolumes: []string{"/tmp/nomad/test-path:/tmp/taskpath"}, 327 }, 328 { 329 name: "clean path local driver", 330 requiresVolumes: true, 331 volumeDriver: "local", 332 volumes: []string{"/tmp/nomad/../test-path:/tmp/taskpath"}, 333 expectedVolumes: []string{"/tmp/test-path:/tmp/taskpath"}, 334 }, 335 } 336 337 t.Run("with volumes enabled", func(t *testing.T) { 338 dh := dockerDriverHarness(t, nil) 339 driver := dh.Impl().(*Driver) 340 driver.config.Volumes.Enabled = true 341 342 for _, c := range cases { 343 t.Run(c.name, func(t *testing.T) { 344 task, cfg, ports := dockerTask(t) 345 defer freeport.Return(ports) 346 cfg.VolumeDriver = c.volumeDriver 347 cfg.Volumes = c.volumes 348 349 task.AllocDir = allocDir 350 task.Name = "demo" 351 352 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 353 354 cc, err := driver.createContainerConfig(task, cfg, "org/repo:0.1") 355 require.NoError(t, err) 356 357 for _, v := range c.expectedVolumes { 358 require.Contains(t, cc.HostConfig.Binds, v) 359 } 360 }) 361 } 362 }) 363 364 t.Run("with volumes disabled", func(t *testing.T) { 365 dh := dockerDriverHarness(t, nil) 366 driver := dh.Impl().(*Driver) 367 driver.config.Volumes.Enabled = false 368 369 for _, c := range cases { 370 t.Run(c.name, func(t *testing.T) { 371 task, cfg, ports := dockerTask(t) 372 defer freeport.Return(ports) 373 cfg.VolumeDriver = c.volumeDriver 374 cfg.Volumes = c.volumes 375 376 task.AllocDir = allocDir 377 task.Name = "demo" 378 379 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 380 381 cc, err := driver.createContainerConfig(task, cfg, "org/repo:0.1") 382 if c.requiresVolumes { 383 require.Error(t, err, "volumes are not enabled") 384 } else { 385 require.NoError(t, err) 386 387 for _, v := range c.expectedVolumes { 388 require.Contains(t, cc.HostConfig.Binds, v) 389 } 390 } 391 }) 392 } 393 }) 394 } 395 396 // This test does not run on windows due to differences in the definition of 397 // an absolute path, changing path expansion behaviour. A similar test should 398 // be written for windows. 399 func TestDockerDriver_MountsSerialization(t *testing.T) { 400 t.Parallel() 401 testutil.DockerCompatible(t) 402 403 allocDir := "/tmp/nomad/alloc-dir" 404 405 cases := []struct { 406 name string 407 requiresVolumes bool 408 passedMounts []DockerMount 409 expectedMounts []docker.HostMount 410 }{ 411 { 412 name: "basic volume", 413 passedMounts: []DockerMount{ 414 { 415 Target: "/nomad", 416 ReadOnly: true, 417 Source: "test", 418 }, 419 }, 420 expectedMounts: []docker.HostMount{ 421 { 422 Type: "volume", 423 Target: "/nomad", 424 Source: "test", 425 ReadOnly: true, 426 VolumeOptions: &docker.VolumeOptions{}, 427 }, 428 }, 429 }, 430 { 431 name: "basic bind", 432 passedMounts: []DockerMount{ 433 { 434 Type: "bind", 435 Target: "/nomad", 436 Source: "test", 437 }, 438 }, 439 expectedMounts: []docker.HostMount{ 440 { 441 Type: "bind", 442 Target: "/nomad", 443 Source: "/tmp/nomad/alloc-dir/demo/test", 444 BindOptions: &docker.BindOptions{}, 445 }, 446 }, 447 }, 448 { 449 name: "basic absolute bind", 450 requiresVolumes: true, 451 passedMounts: []DockerMount{ 452 { 453 Type: "bind", 454 Target: "/nomad", 455 Source: "/tmp/test", 456 }, 457 }, 458 expectedMounts: []docker.HostMount{ 459 { 460 Type: "bind", 461 Target: "/nomad", 462 Source: "/tmp/test", 463 BindOptions: &docker.BindOptions{}, 464 }, 465 }, 466 }, 467 { 468 name: "bind relative outside", 469 requiresVolumes: true, 470 passedMounts: []DockerMount{ 471 { 472 Type: "bind", 473 Target: "/nomad", 474 Source: "../../test", 475 }, 476 }, 477 expectedMounts: []docker.HostMount{ 478 { 479 Type: "bind", 480 Target: "/nomad", 481 Source: "/tmp/nomad/test", 482 BindOptions: &docker.BindOptions{}, 483 }, 484 }, 485 }, 486 { 487 name: "basic tmpfs", 488 requiresVolumes: false, 489 passedMounts: []DockerMount{ 490 { 491 Type: "tmpfs", 492 Target: "/nomad", 493 TmpfsOptions: DockerTmpfsOptions{ 494 SizeBytes: 321, 495 Mode: 0666, 496 }, 497 }, 498 }, 499 expectedMounts: []docker.HostMount{ 500 { 501 Type: "tmpfs", 502 Target: "/nomad", 503 TempfsOptions: &docker.TempfsOptions{ 504 SizeBytes: 321, 505 Mode: 0666, 506 }, 507 }, 508 }, 509 }, 510 } 511 512 t.Run("with volumes enabled", func(t *testing.T) { 513 dh := dockerDriverHarness(t, nil) 514 driver := dh.Impl().(*Driver) 515 driver.config.Volumes.Enabled = true 516 517 for _, c := range cases { 518 t.Run(c.name, func(t *testing.T) { 519 task, cfg, ports := dockerTask(t) 520 defer freeport.Return(ports) 521 cfg.Mounts = c.passedMounts 522 523 task.AllocDir = allocDir 524 task.Name = "demo" 525 526 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 527 528 cc, err := driver.createContainerConfig(task, cfg, "org/repo:0.1") 529 require.NoError(t, err) 530 require.EqualValues(t, c.expectedMounts, cc.HostConfig.Mounts) 531 }) 532 } 533 }) 534 535 t.Run("with volumes disabled", func(t *testing.T) { 536 dh := dockerDriverHarness(t, nil) 537 driver := dh.Impl().(*Driver) 538 driver.config.Volumes.Enabled = false 539 540 for _, c := range cases { 541 t.Run(c.name, func(t *testing.T) { 542 task, cfg, ports := dockerTask(t) 543 defer freeport.Return(ports) 544 cfg.Mounts = c.passedMounts 545 546 task.AllocDir = allocDir 547 task.Name = "demo" 548 549 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 550 551 cc, err := driver.createContainerConfig(task, cfg, "org/repo:0.1") 552 if c.requiresVolumes { 553 require.Error(t, err, "volumes are not enabled") 554 } else { 555 require.NoError(t, err) 556 require.EqualValues(t, c.expectedMounts, cc.HostConfig.Mounts) 557 } 558 }) 559 } 560 }) 561 } 562 563 // TestDockerDriver_CreateContainerConfig_MountsCombined asserts that 564 // devices and mounts set by device managers/plugins are honored 565 // and present in docker.CreateContainerOptions, and that it is appended 566 // to any devices/mounts a user sets in the task config. 567 func TestDockerDriver_CreateContainerConfig_MountsCombined(t *testing.T) { 568 t.Parallel() 569 testutil.DockerCompatible(t) 570 571 task, cfg, ports := dockerTask(t) 572 defer freeport.Return(ports) 573 574 task.Devices = []*drivers.DeviceConfig{ 575 { 576 HostPath: "/dev/fuse", 577 TaskPath: "/container/dev/task-fuse", 578 Permissions: "rw", 579 }, 580 } 581 task.Mounts = []*drivers.MountConfig{ 582 { 583 HostPath: "/tmp/task-mount", 584 TaskPath: "/container/tmp/task-mount", 585 Readonly: true, 586 }, 587 } 588 589 cfg.Devices = []DockerDevice{ 590 { 591 HostPath: "/dev/stdout", 592 ContainerPath: "/container/dev/cfg-stdout", 593 CgroupPermissions: "rwm", 594 }, 595 } 596 cfg.Mounts = []DockerMount{ 597 { 598 Type: "bind", 599 Source: "/tmp/cfg-mount", 600 Target: "/container/tmp/cfg-mount", 601 ReadOnly: false, 602 }, 603 } 604 605 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 606 607 dh := dockerDriverHarness(t, nil) 608 driver := dh.Impl().(*Driver) 609 610 c, err := driver.createContainerConfig(task, cfg, "org/repo:0.1") 611 require.NoError(t, err) 612 expectedMounts := []docker.HostMount{ 613 { 614 Type: "bind", 615 Source: "/tmp/cfg-mount", 616 Target: "/container/tmp/cfg-mount", 617 ReadOnly: false, 618 BindOptions: &docker.BindOptions{ 619 Propagation: "", 620 }, 621 }, 622 { 623 Type: "bind", 624 Source: "/tmp/task-mount", 625 Target: "/container/tmp/task-mount", 626 ReadOnly: true, 627 BindOptions: &docker.BindOptions{ 628 Propagation: "rprivate", 629 }, 630 }, 631 } 632 633 if runtime.GOOS != "linux" { 634 expectedMounts[0].BindOptions = &docker.BindOptions{} 635 expectedMounts[1].BindOptions = &docker.BindOptions{} 636 } 637 638 foundMounts := c.HostConfig.Mounts 639 sort.Slice(foundMounts, func(i, j int) bool { 640 return foundMounts[i].Target < foundMounts[j].Target 641 }) 642 require.EqualValues(t, expectedMounts, foundMounts) 643 644 expectedDevices := []docker.Device{ 645 { 646 PathOnHost: "/dev/stdout", 647 PathInContainer: "/container/dev/cfg-stdout", 648 CgroupPermissions: "rwm", 649 }, 650 { 651 PathOnHost: "/dev/fuse", 652 PathInContainer: "/container/dev/task-fuse", 653 CgroupPermissions: "rw", 654 }, 655 } 656 657 foundDevices := c.HostConfig.Devices 658 sort.Slice(foundDevices, func(i, j int) bool { 659 return foundDevices[i].PathInContainer < foundDevices[j].PathInContainer 660 }) 661 require.EqualValues(t, expectedDevices, foundDevices) 662 } 663 664 // TestDockerDriver_Cleanup ensures Cleanup removes only downloaded images. 665 // Doesn't run on windows because it requires an image variant 666 func TestDockerDriver_Cleanup(t *testing.T) { 667 testutil.DockerCompatible(t) 668 669 // using a small image and an specific point release to avoid accidental conflicts with other tasks 670 cfg := newTaskConfig("", []string{"sleep", "100"}) 671 cfg.Image = "busybox:1.29.2" 672 cfg.LoadImage = "" 673 task := &drivers.TaskConfig{ 674 ID: uuid.Generate(), 675 Name: "cleanup_test", 676 Resources: basicResources, 677 } 678 679 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 680 681 client, driver, handle, cleanup := dockerSetup(t, task, map[string]interface{}{ 682 "gc": map[string]interface{}{ 683 "image": true, 684 "image_delay": "1ms", 685 }, 686 }) 687 defer cleanup() 688 689 require.NoError(t, driver.WaitUntilStarted(task.ID, 5*time.Second)) 690 // Cleanup 691 require.NoError(t, driver.DestroyTask(task.ID, true)) 692 693 // Ensure image was removed 694 tu.WaitForResult(func() (bool, error) { 695 if _, err := client.InspectImage(cfg.Image); err == nil { 696 return false, fmt.Errorf("image exists but should have been removed. Does another %v container exist?", cfg.Image) 697 } 698 699 return true, nil 700 }, func(err error) { 701 require.NoError(t, err) 702 }) 703 704 // The image doesn't exist which shouldn't be an error when calling 705 // Cleanup, so call it again to make sure. 706 require.NoError(t, driver.Impl().(*Driver).cleanupImage(handle)) 707 } 708 709 // Tests that images prefixed with "https://" are supported 710 func TestDockerDriver_Start_Image_HTTPS(t *testing.T) { 711 if !tu.IsCI() { 712 t.Parallel() 713 } 714 testutil.DockerCompatible(t) 715 716 taskCfg := TaskConfig{ 717 Image: "https://gcr.io/google_containers/pause:0.8.0", 718 } 719 task := &drivers.TaskConfig{ 720 ID: uuid.Generate(), 721 Name: "pause", 722 AllocID: uuid.Generate(), 723 Resources: basicResources, 724 } 725 require.NoError(t, task.EncodeConcreteDriverConfig(&taskCfg)) 726 727 d := dockerDriverHarness(t, nil) 728 cleanup := d.MkAllocDir(task, true) 729 defer cleanup() 730 731 _, _, err := d.StartTask(task) 732 require.NoError(t, err) 733 734 d.DestroyTask(task.ID, true) 735 } 736 737 func newTaskConfig(variant string, command []string) TaskConfig { 738 // busyboxImageID is the ID stored in busybox.tar 739 busyboxImageID := "busybox:1.29.3" 740 741 image := busyboxImageID 742 loadImage := "busybox.tar" 743 if variant != "" { 744 image = fmt.Sprintf("%s-%s", busyboxImageID, variant) 745 loadImage = fmt.Sprintf("busybox_%s.tar", variant) 746 } 747 748 return TaskConfig{ 749 Image: image, 750 LoadImage: loadImage, 751 Command: command[0], 752 Args: command[1:], 753 } 754 } 755 756 func copyImage(t *testing.T, taskDir *allocdir.TaskDir, image string) { 757 dst := filepath.Join(taskDir.LocalDir, image) 758 copyFile(filepath.Join("./test-resources/docker", image), dst, t) 759 } 760 761 // copyFile moves an existing file to the destination 762 func copyFile(src, dst string, t *testing.T) { 763 in, err := os.Open(src) 764 if err != nil { 765 t.Fatalf("copying %v -> %v failed: %v", src, dst, err) 766 } 767 defer in.Close() 768 out, err := os.Create(dst) 769 if err != nil { 770 t.Fatalf("copying %v -> %v failed: %v", src, dst, err) 771 } 772 defer func() { 773 if err := out.Close(); err != nil { 774 t.Fatalf("copying %v -> %v failed: %v", src, dst, err) 775 } 776 }() 777 if _, err = io.Copy(out, in); err != nil { 778 t.Fatalf("copying %v -> %v failed: %v", src, dst, err) 779 } 780 if err := out.Sync(); err != nil { 781 t.Fatalf("copying %v -> %v failed: %v", src, dst, err) 782 } 783 } 784 785 func TestDocker_ExecTaskStreaming(t *testing.T) { 786 if !tu.IsCI() { 787 t.Parallel() 788 } 789 testutil.DockerCompatible(t) 790 791 taskCfg := newTaskConfig("", []string{"/bin/sleep", "1000"}) 792 task := &drivers.TaskConfig{ 793 ID: uuid.Generate(), 794 Name: "nc-demo", 795 AllocID: uuid.Generate(), 796 Resources: basicResources, 797 } 798 require.NoError(t, task.EncodeConcreteDriverConfig(&taskCfg)) 799 800 d := dockerDriverHarness(t, nil) 801 cleanup := d.MkAllocDir(task, true) 802 defer cleanup() 803 copyImage(t, task.TaskDir(), "busybox.tar") 804 805 _, _, err := d.StartTask(task) 806 require.NoError(t, err) 807 808 defer d.DestroyTask(task.ID, true) 809 810 dtestutil.ExecTaskStreamingConformanceTests(t, d, task.ID) 811 812 }