github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/client/driver/docker_test.go (about) 1 package driver 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "math/rand" 7 "os" 8 "path/filepath" 9 "reflect" 10 "runtime/debug" 11 "strconv" 12 "strings" 13 "syscall" 14 "testing" 15 "time" 16 17 docker "github.com/fsouza/go-dockerclient" 18 "github.com/ncodes/nomad/client/allocdir" 19 "github.com/ncodes/nomad/client/config" 20 "github.com/ncodes/nomad/client/driver/env" 21 cstructs "github.com/ncodes/nomad/client/structs" 22 "github.com/ncodes/nomad/client/testutil" 23 "github.com/ncodes/nomad/nomad/mock" 24 "github.com/ncodes/nomad/nomad/structs" 25 tu "github.com/ncodes/nomad/testutil" 26 ) 27 28 func dockerIsRemote(t *testing.T) bool { 29 client, err := docker.NewClientFromEnv() 30 if err != nil { 31 return false 32 } 33 34 // Technically this could be a local tcp socket but for testing purposes 35 // we'll just assume that tcp is only used for remote connections. 36 if client.Endpoint()[0:3] == "tcp" { 37 return true 38 } 39 return false 40 } 41 42 // Ports used by tests 43 var ( 44 docker_reserved = 32768 + int(rand.Int31n(25000)) 45 docker_dynamic = 32768 + int(rand.Int31n(25000)) 46 ) 47 48 // Returns a task with a reserved and dynamic port. The ports are returned 49 // respectively. 50 func dockerTask() (*structs.Task, int, int) { 51 docker_reserved += 1 52 docker_dynamic += 1 53 return &structs.Task{ 54 Name: "redis-demo", 55 Driver: "docker", 56 Config: map[string]interface{}{ 57 "image": "busybox", 58 "load": "busybox.tar", 59 "command": "/bin/nc", 60 "args": []string{"-l", "127.0.0.1", "-p", "0"}, 61 }, 62 LogConfig: &structs.LogConfig{ 63 MaxFiles: 10, 64 MaxFileSizeMB: 10, 65 }, 66 Resources: &structs.Resources{ 67 MemoryMB: 256, 68 CPU: 512, 69 Networks: []*structs.NetworkResource{ 70 &structs.NetworkResource{ 71 IP: "127.0.0.1", 72 ReservedPorts: []structs.Port{{Label: "main", Value: docker_reserved}}, 73 DynamicPorts: []structs.Port{{Label: "REDIS", Value: docker_dynamic}}, 74 }, 75 }, 76 }, 77 }, docker_reserved, docker_dynamic 78 } 79 80 // dockerSetup does all of the basic setup you need to get a running docker 81 // process up and running for testing. Use like: 82 // 83 // task := taskTemplate() 84 // // do custom task configuration 85 // client, handle, cleanup := dockerSetup(t, task) 86 // defer cleanup() 87 // // do test stuff 88 // 89 // If there is a problem during setup this function will abort or skip the test 90 // and indicate the reason. 91 func dockerSetup(t *testing.T, task *structs.Task) (*docker.Client, DriverHandle, func()) { 92 client := newTestDockerClient(t) 93 return dockerSetupWithClient(t, task, client) 94 } 95 96 func testDockerDriverContexts(t *testing.T, task *structs.Task) *testContext { 97 tctx := testDriverContexts(t, task) 98 99 // Drop the delay 100 tctx.DriverCtx.config.Options = make(map[string]string) 101 tctx.DriverCtx.config.Options[dockerImageRemoveDelayConfigOption] = "1s" 102 103 return tctx 104 } 105 106 func dockerSetupWithClient(t *testing.T, task *structs.Task, client *docker.Client) (*docker.Client, DriverHandle, func()) { 107 tctx := testDockerDriverContexts(t, task) 108 //tctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 109 driver := NewDockerDriver(tctx.DriverCtx) 110 copyImage(t, tctx.ExecCtx.TaskDir, "busybox.tar") 111 112 res, err := driver.Prestart(tctx.ExecCtx, task) 113 if err != nil { 114 tctx.AllocDir.Destroy() 115 t.Fatalf("error in prestart: %v", err) 116 } 117 118 handle, err := driver.Start(tctx.ExecCtx, task) 119 if err != nil { 120 tctx.AllocDir.Destroy() 121 t.Fatalf("Failed to start driver: %s\nStack\n%s", err, debug.Stack()) 122 } 123 124 if handle == nil { 125 tctx.AllocDir.Destroy() 126 t.Fatalf("handle is nil\nStack\n%s", debug.Stack()) 127 } 128 129 cleanup := func() { 130 driver.Cleanup(tctx.ExecCtx, res) 131 handle.Kill() 132 tctx.AllocDir.Destroy() 133 } 134 135 return client, handle, cleanup 136 } 137 138 func newTestDockerClient(t *testing.T) *docker.Client { 139 if !testutil.DockerIsConnected(t) { 140 t.SkipNow() 141 } 142 143 client, err := docker.NewClientFromEnv() 144 if err != nil { 145 t.Fatalf("Failed to initialize client: %s\nStack\n%s", err, debug.Stack()) 146 } 147 return client 148 } 149 150 // This test should always pass, even if docker daemon is not available 151 func TestDockerDriver_Fingerprint(t *testing.T) { 152 ctx := testDockerDriverContexts(t, &structs.Task{Name: "foo", Driver: "docker", Resources: basicResources}) 153 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 154 defer ctx.AllocDir.Destroy() 155 d := NewDockerDriver(ctx.DriverCtx) 156 node := &structs.Node{ 157 Attributes: make(map[string]string), 158 } 159 apply, err := d.Fingerprint(&config.Config{}, node) 160 if err != nil { 161 t.Fatalf("err: %v", err) 162 } 163 if apply != testutil.DockerIsConnected(t) { 164 t.Fatalf("Fingerprinter should detect when docker is available") 165 } 166 if node.Attributes["driver.docker"] != "1" { 167 t.Log("Docker daemon not available. The remainder of the docker tests will be skipped.") 168 } 169 t.Logf("Found docker version %s", node.Attributes["driver.docker.version"]) 170 } 171 172 func TestDockerDriver_StartOpen_Wait(t *testing.T) { 173 if !testutil.DockerIsConnected(t) { 174 t.SkipNow() 175 } 176 177 task := &structs.Task{ 178 Name: "nc-demo", 179 Driver: "docker", 180 Config: map[string]interface{}{ 181 "load": "busybox.tar", 182 "image": "busybox", 183 "command": "/bin/nc", 184 "args": []string{"-l", "127.0.0.1", "-p", "0"}, 185 }, 186 LogConfig: &structs.LogConfig{ 187 MaxFiles: 10, 188 MaxFileSizeMB: 10, 189 }, 190 Resources: basicResources, 191 } 192 193 ctx := testDockerDriverContexts(t, task) 194 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 195 defer ctx.AllocDir.Destroy() 196 d := NewDockerDriver(ctx.DriverCtx) 197 copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar") 198 199 _, err := d.Prestart(ctx.ExecCtx, task) 200 if err != nil { 201 t.Fatalf("error in prestart: %v", err) 202 } 203 204 handle, err := d.Start(ctx.ExecCtx, task) 205 if err != nil { 206 t.Fatalf("err: %v", err) 207 } 208 if handle == nil { 209 t.Fatalf("missing handle") 210 } 211 defer handle.Kill() 212 213 // Attempt to open 214 handle2, err := d.Open(ctx.ExecCtx, handle.ID()) 215 if err != nil { 216 t.Fatalf("err: %v", err) 217 } 218 if handle2 == nil { 219 t.Fatalf("missing handle") 220 } 221 } 222 223 func TestDockerDriver_Start_Wait(t *testing.T) { 224 task := &structs.Task{ 225 Name: "nc-demo", 226 Driver: "docker", 227 Config: map[string]interface{}{ 228 "load": "busybox.tar", 229 "image": "busybox", 230 "command": "/bin/echo", 231 "args": []string{"hello"}, 232 }, 233 Resources: &structs.Resources{ 234 MemoryMB: 256, 235 CPU: 512, 236 }, 237 LogConfig: &structs.LogConfig{ 238 MaxFiles: 10, 239 MaxFileSizeMB: 10, 240 }, 241 } 242 243 _, handle, cleanup := dockerSetup(t, task) 244 defer cleanup() 245 246 // Update should be a no-op 247 err := handle.Update(task) 248 if err != nil { 249 t.Fatalf("err: %v", err) 250 } 251 252 select { 253 case res := <-handle.WaitCh(): 254 if !res.Successful() { 255 t.Fatalf("err: %v", res) 256 } 257 case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second): 258 t.Fatalf("timeout") 259 } 260 } 261 262 func TestDockerDriver_Start_LoadImage(t *testing.T) { 263 if !testutil.DockerIsConnected(t) { 264 t.SkipNow() 265 } 266 task := &structs.Task{ 267 Name: "busybox-demo", 268 Driver: "docker", 269 Config: map[string]interface{}{ 270 "image": "busybox", 271 "load": "busybox.tar", 272 "command": "/bin/echo", 273 "args": []string{ 274 "hello", 275 }, 276 }, 277 LogConfig: &structs.LogConfig{ 278 MaxFiles: 10, 279 MaxFileSizeMB: 10, 280 }, 281 Resources: &structs.Resources{ 282 MemoryMB: 256, 283 CPU: 512, 284 }, 285 } 286 287 ctx := testDockerDriverContexts(t, task) 288 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 289 defer ctx.AllocDir.Destroy() 290 d := NewDockerDriver(ctx.DriverCtx) 291 292 // Copy the image into the task's directory 293 copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar") 294 295 _, err := d.Prestart(ctx.ExecCtx, task) 296 if err != nil { 297 t.Fatalf("error in prestart: %v", err) 298 } 299 handle, err := d.Start(ctx.ExecCtx, task) 300 if err != nil { 301 t.Fatalf("err: %v", err) 302 } 303 if handle == nil { 304 t.Fatalf("missing handle") 305 } 306 defer handle.Kill() 307 308 select { 309 case res := <-handle.WaitCh(): 310 if !res.Successful() { 311 t.Fatalf("err: %v", res) 312 } 313 case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second): 314 t.Fatalf("timeout") 315 } 316 317 // Check that data was written to the shared alloc directory. 318 outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "busybox-demo.stdout.0") 319 act, err := ioutil.ReadFile(outputFile) 320 if err != nil { 321 t.Fatalf("Couldn't read expected output: %v", err) 322 } 323 324 exp := "hello" 325 if strings.TrimSpace(string(act)) != exp { 326 t.Fatalf("Command outputted %v; want %v", act, exp) 327 } 328 329 } 330 331 func TestDockerDriver_Start_BadPull_Recoverable(t *testing.T) { 332 if !testutil.DockerIsConnected(t) { 333 t.SkipNow() 334 } 335 task := &structs.Task{ 336 Name: "busybox-demo", 337 Driver: "docker", 338 Config: map[string]interface{}{ 339 "image": "127.0.1.1:32121/foo", // bad path 340 "command": "/bin/echo", 341 "args": []string{ 342 "hello", 343 }, 344 }, 345 LogConfig: &structs.LogConfig{ 346 MaxFiles: 10, 347 MaxFileSizeMB: 10, 348 }, 349 Resources: &structs.Resources{ 350 MemoryMB: 256, 351 CPU: 512, 352 }, 353 } 354 355 ctx := testDockerDriverContexts(t, task) 356 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 357 defer ctx.AllocDir.Destroy() 358 d := NewDockerDriver(ctx.DriverCtx) 359 360 _, err := d.Prestart(ctx.ExecCtx, task) 361 if err == nil { 362 t.Fatalf("want error in prestart: %v", err) 363 } 364 365 if rerr, ok := err.(*structs.RecoverableError); !ok { 366 t.Fatalf("want recoverable error: %+v", err) 367 } else if !rerr.IsRecoverable() { 368 t.Fatalf("error not recoverable: %+v", err) 369 } 370 } 371 372 func TestDockerDriver_Start_Wait_AllocDir(t *testing.T) { 373 // This test requires that the alloc dir be mounted into docker as a volume. 374 // Because this cannot happen when docker is run remotely, e.g. when running 375 // docker in a VM, we skip this when we detect Docker is being run remotely. 376 if !testutil.DockerIsConnected(t) || dockerIsRemote(t) { 377 t.SkipNow() 378 } 379 380 exp := []byte{'w', 'i', 'n'} 381 file := "output.txt" 382 task := &structs.Task{ 383 Name: "nc-demo", 384 Driver: "docker", 385 Config: map[string]interface{}{ 386 "image": "busybox", 387 "load": "busybox.tar", 388 "command": "/bin/sh", 389 "args": []string{ 390 "-c", 391 fmt.Sprintf(`sleep 1; echo -n %s > $%s/%s`, 392 string(exp), env.AllocDir, file), 393 }, 394 }, 395 LogConfig: &structs.LogConfig{ 396 MaxFiles: 10, 397 MaxFileSizeMB: 10, 398 }, 399 Resources: &structs.Resources{ 400 MemoryMB: 256, 401 CPU: 512, 402 }, 403 } 404 405 ctx := testDockerDriverContexts(t, task) 406 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 407 defer ctx.AllocDir.Destroy() 408 d := NewDockerDriver(ctx.DriverCtx) 409 copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar") 410 411 _, err := d.Prestart(ctx.ExecCtx, task) 412 if err != nil { 413 t.Fatalf("error in prestart: %v", err) 414 } 415 handle, err := d.Start(ctx.ExecCtx, task) 416 if err != nil { 417 t.Fatalf("err: %v", err) 418 } 419 if handle == nil { 420 t.Fatalf("missing handle") 421 } 422 defer handle.Kill() 423 424 select { 425 case res := <-handle.WaitCh(): 426 if !res.Successful() { 427 t.Fatalf("err: %v", res) 428 } 429 case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second): 430 t.Fatalf("timeout") 431 } 432 433 // Check that data was written to the shared alloc directory. 434 outputFile := filepath.Join(ctx.AllocDir.SharedDir, file) 435 act, err := ioutil.ReadFile(outputFile) 436 if err != nil { 437 t.Fatalf("Couldn't read expected output: %v", err) 438 } 439 440 if !reflect.DeepEqual(act, exp) { 441 t.Fatalf("Command outputted %v; want %v", act, exp) 442 } 443 } 444 445 func TestDockerDriver_Start_Kill_Wait(t *testing.T) { 446 task := &structs.Task{ 447 Name: "nc-demo", 448 Driver: "docker", 449 Config: map[string]interface{}{ 450 "image": "busybox", 451 "load": "busybox.tar", 452 "command": "/bin/sleep", 453 "args": []string{"10"}, 454 }, 455 LogConfig: &structs.LogConfig{ 456 MaxFiles: 10, 457 MaxFileSizeMB: 10, 458 }, 459 Resources: basicResources, 460 } 461 462 _, handle, cleanup := dockerSetup(t, task) 463 defer cleanup() 464 465 go func() { 466 time.Sleep(100 * time.Millisecond) 467 err := handle.Kill() 468 if err != nil { 469 t.Fatalf("err: %v", err) 470 } 471 }() 472 473 select { 474 case res := <-handle.WaitCh(): 475 if res.Successful() { 476 t.Fatalf("should err: %v", res) 477 } 478 case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second): 479 t.Fatalf("timeout") 480 } 481 } 482 483 func TestDockerDriver_StartN(t *testing.T) { 484 if !testutil.DockerIsConnected(t) { 485 t.SkipNow() 486 } 487 488 task1, _, _ := dockerTask() 489 task2, _, _ := dockerTask() 490 task3, _, _ := dockerTask() 491 taskList := []*structs.Task{task1, task2, task3} 492 493 handles := make([]DriverHandle, len(taskList)) 494 495 t.Logf("Starting %d tasks", len(taskList)) 496 497 // Let's spin up a bunch of things 498 for idx, task := range taskList { 499 ctx := testDockerDriverContexts(t, task) 500 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 501 defer ctx.AllocDir.Destroy() 502 d := NewDockerDriver(ctx.DriverCtx) 503 copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar") 504 505 _, err := d.Prestart(ctx.ExecCtx, task) 506 if err != nil { 507 t.Fatalf("error in prestart #%d: %v", idx+1, err) 508 } 509 handles[idx], err = d.Start(ctx.ExecCtx, task) 510 if err != nil { 511 t.Errorf("Failed starting task #%d: %s", idx+1, err) 512 } 513 } 514 515 t.Log("All tasks are started. Terminating...") 516 517 for idx, handle := range handles { 518 if handle == nil { 519 t.Errorf("Bad handle for task #%d", idx+1) 520 continue 521 } 522 523 err := handle.Kill() 524 if err != nil { 525 t.Errorf("Failed stopping task #%d: %s", idx+1, err) 526 } 527 } 528 529 t.Log("Test complete!") 530 } 531 532 func TestDockerDriver_StartNVersions(t *testing.T) { 533 if !testutil.DockerIsConnected(t) { 534 t.SkipNow() 535 } 536 537 task1, _, _ := dockerTask() 538 task1.Config["image"] = "busybox" 539 task1.Config["load"] = "busybox.tar" 540 541 task2, _, _ := dockerTask() 542 task2.Config["image"] = "busybox:musl" 543 task2.Config["load"] = "busybox_musl.tar" 544 545 task3, _, _ := dockerTask() 546 task3.Config["image"] = "busybox:glibc" 547 task3.Config["load"] = "busybox_glibc.tar" 548 549 taskList := []*structs.Task{task1, task2, task3} 550 551 handles := make([]DriverHandle, len(taskList)) 552 553 t.Logf("Starting %d tasks", len(taskList)) 554 555 // Let's spin up a bunch of things 556 for idx, task := range taskList { 557 ctx := testDockerDriverContexts(t, task) 558 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 559 defer ctx.AllocDir.Destroy() 560 d := NewDockerDriver(ctx.DriverCtx) 561 copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar") 562 copyImage(t, ctx.ExecCtx.TaskDir, "busybox_musl.tar") 563 copyImage(t, ctx.ExecCtx.TaskDir, "busybox_glibc.tar") 564 565 _, err := d.Prestart(ctx.ExecCtx, task) 566 if err != nil { 567 t.Fatalf("error in prestart #%d: %v", idx+1, err) 568 } 569 handles[idx], err = d.Start(ctx.ExecCtx, task) 570 if err != nil { 571 t.Errorf("Failed starting task #%d: %s", idx+1, err) 572 } 573 } 574 575 t.Log("All tasks are started. Terminating...") 576 577 for idx, handle := range handles { 578 if handle == nil { 579 t.Errorf("Bad handle for task #%d", idx+1) 580 continue 581 } 582 583 err := handle.Kill() 584 if err != nil { 585 t.Errorf("Failed stopping task #%d: %s", idx+1, err) 586 } 587 } 588 589 t.Log("Test complete!") 590 } 591 592 func waitForExist(t *testing.T, client *docker.Client, handle *DockerHandle) { 593 tu.WaitForResult(func() (bool, error) { 594 container, err := client.InspectContainer(handle.ContainerID()) 595 if err != nil { 596 if _, ok := err.(*docker.NoSuchContainer); !ok { 597 return false, err 598 } 599 } 600 601 return container != nil, nil 602 }, func(err error) { 603 t.Fatalf("err: %v", err) 604 }) 605 } 606 607 func TestDockerDriver_NetworkMode_Host(t *testing.T) { 608 expected := "host" 609 610 task := &structs.Task{ 611 Name: "nc-demo", 612 Driver: "docker", 613 Config: map[string]interface{}{ 614 "image": "busybox", 615 "load": "busybox.tar", 616 "command": "/bin/nc", 617 "args": []string{"-l", "127.0.0.1", "-p", "0"}, 618 "network_mode": expected, 619 }, 620 Resources: &structs.Resources{ 621 MemoryMB: 256, 622 CPU: 512, 623 }, 624 LogConfig: &structs.LogConfig{ 625 MaxFiles: 10, 626 MaxFileSizeMB: 10, 627 }, 628 } 629 630 client, handle, cleanup := dockerSetup(t, task) 631 defer cleanup() 632 633 waitForExist(t, client, handle.(*DockerHandle)) 634 635 container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID()) 636 if err != nil { 637 t.Fatalf("err: %v", err) 638 } 639 640 actual := container.HostConfig.NetworkMode 641 if actual != expected { 642 t.Fatalf("Got network mode %q; want %q", expected, actual) 643 } 644 } 645 646 func TestDockerDriver_NetworkAliases_Bridge(t *testing.T) { 647 // Because go-dockerclient doesn't provide api for query network aliases, just check that 648 // a container can be created with a 'network_aliases' property 649 650 // Create network, network-scoped alias is supported only for containers in user defined networks 651 client := newTestDockerClient(t) 652 networkOpts := docker.CreateNetworkOptions{Name: "foobar", Driver: "bridge"} 653 network, err := client.CreateNetwork(networkOpts) 654 if err != nil { 655 t.Fatalf("err: %v", err) 656 } 657 defer client.RemoveNetwork(network.ID) 658 659 expected := []string{"foobar"} 660 task := &structs.Task{ 661 Name: "nc-demo", 662 Driver: "docker", 663 Config: map[string]interface{}{ 664 "image": "busybox", 665 "load": "busybox.tar", 666 "command": "/bin/nc", 667 "args": []string{"-l", "127.0.0.1", "-p", "0"}, 668 "network_mode": network.Name, 669 "network_aliases": expected, 670 }, 671 Resources: &structs.Resources{ 672 MemoryMB: 256, 673 CPU: 512, 674 }, 675 LogConfig: &structs.LogConfig{ 676 MaxFiles: 10, 677 MaxFileSizeMB: 10, 678 }, 679 } 680 681 client, handle, cleanup := dockerSetupWithClient(t, task, client) 682 defer cleanup() 683 684 waitForExist(t, client, handle.(*DockerHandle)) 685 686 _, err = client.InspectContainer(handle.(*DockerHandle).ContainerID()) 687 if err != nil { 688 t.Fatalf("err: %v", err) 689 } 690 } 691 692 func TestDockerDriver_Labels(t *testing.T) { 693 task, _, _ := dockerTask() 694 task.Config["labels"] = []map[string]string{ 695 map[string]string{ 696 "label1": "value1", 697 "label2": "value2", 698 }, 699 } 700 701 client, handle, cleanup := dockerSetup(t, task) 702 defer cleanup() 703 704 waitForExist(t, client, handle.(*DockerHandle)) 705 706 container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID()) 707 if err != nil { 708 t.Fatalf("err: %v", err) 709 } 710 711 if want, got := 2, len(container.Config.Labels); want != got { 712 t.Errorf("Wrong labels count for docker job. Expect: %d, got: %d", want, got) 713 } 714 715 if want, got := "value1", container.Config.Labels["label1"]; want != got { 716 t.Errorf("Wrong label value docker job. Expect: %s, got: %s", want, got) 717 } 718 } 719 720 func TestDockerDriver_ForcePull_IsInvalidConfig(t *testing.T) { 721 task, _, _ := dockerTask() 722 task.Config["force_pull"] = "nothing" 723 724 ctx := testDockerDriverContexts(t, task) 725 defer ctx.AllocDir.Destroy() 726 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 727 driver := NewDockerDriver(ctx.DriverCtx) 728 729 if _, err := driver.Prestart(ctx.ExecCtx, task); err == nil { 730 t.Fatalf("error expected in prestart") 731 } 732 } 733 734 func TestDockerDriver_ForcePull(t *testing.T) { 735 task, _, _ := dockerTask() 736 task.Config["force_pull"] = "true" 737 738 client, handle, cleanup := dockerSetup(t, task) 739 defer cleanup() 740 741 waitForExist(t, client, handle.(*DockerHandle)) 742 743 _, err := client.InspectContainer(handle.(*DockerHandle).ContainerID()) 744 if err != nil { 745 t.Fatalf("err: %v", err) 746 } 747 } 748 749 func TestDockerDriver_DNS(t *testing.T) { 750 task, _, _ := dockerTask() 751 task.Config["dns_servers"] = []string{"8.8.8.8", "8.8.4.4"} 752 task.Config["dns_search_domains"] = []string{"example.com", "example.org", "example.net"} 753 754 client, handle, cleanup := dockerSetup(t, task) 755 defer cleanup() 756 757 waitForExist(t, client, handle.(*DockerHandle)) 758 759 container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID()) 760 if err != nil { 761 t.Fatalf("err: %v", err) 762 } 763 764 if !reflect.DeepEqual(task.Config["dns_servers"], container.HostConfig.DNS) { 765 t.Errorf("DNS Servers don't match.\nExpected:\n%s\nGot:\n%s\n", task.Config["dns_servers"], container.HostConfig.DNS) 766 } 767 768 if !reflect.DeepEqual(task.Config["dns_search_domains"], container.HostConfig.DNSSearch) { 769 t.Errorf("DNS Servers don't match.\nExpected:\n%s\nGot:\n%s\n", task.Config["dns_search_domains"], container.HostConfig.DNSSearch) 770 } 771 } 772 773 func TestDockerWorkDir(t *testing.T) { 774 task, _, _ := dockerTask() 775 task.Config["work_dir"] = "/some/path" 776 777 client, handle, cleanup := dockerSetup(t, task) 778 defer cleanup() 779 780 container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID()) 781 if err != nil { 782 t.Fatalf("err: %v", err) 783 } 784 785 if want, got := "/some/path", container.Config.WorkingDir; want != got { 786 t.Errorf("Wrong working directory for docker job. Expect: %s, got: %s", want, got) 787 } 788 } 789 790 func inSlice(needle string, haystack []string) bool { 791 for _, h := range haystack { 792 if h == needle { 793 return true 794 } 795 } 796 return false 797 } 798 799 func TestDockerDriver_PortsNoMap(t *testing.T) { 800 task, res, dyn := dockerTask() 801 802 client, handle, cleanup := dockerSetup(t, task) 803 defer cleanup() 804 805 waitForExist(t, client, handle.(*DockerHandle)) 806 807 container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID()) 808 if err != nil { 809 t.Fatalf("err: %v", err) 810 } 811 812 // Verify that the correct ports are EXPOSED 813 expectedExposedPorts := map[docker.Port]struct{}{ 814 docker.Port(fmt.Sprintf("%d/tcp", res)): struct{}{}, 815 docker.Port(fmt.Sprintf("%d/udp", res)): struct{}{}, 816 docker.Port(fmt.Sprintf("%d/tcp", dyn)): struct{}{}, 817 docker.Port(fmt.Sprintf("%d/udp", dyn)): struct{}{}, 818 } 819 820 if !reflect.DeepEqual(container.Config.ExposedPorts, expectedExposedPorts) { 821 t.Errorf("Exposed ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedExposedPorts, container.Config.ExposedPorts) 822 } 823 824 // Verify that the correct ports are FORWARDED 825 expectedPortBindings := map[docker.Port][]docker.PortBinding{ 826 docker.Port(fmt.Sprintf("%d/tcp", res)): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}}, 827 docker.Port(fmt.Sprintf("%d/udp", res)): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}}, 828 docker.Port(fmt.Sprintf("%d/tcp", dyn)): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}}, 829 docker.Port(fmt.Sprintf("%d/udp", dyn)): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}}, 830 } 831 832 if !reflect.DeepEqual(container.HostConfig.PortBindings, expectedPortBindings) { 833 t.Errorf("Forwarded ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedPortBindings, container.HostConfig.PortBindings) 834 } 835 836 expectedEnvironment := map[string]string{ 837 "NOMAD_ADDR_main": fmt.Sprintf("127.0.0.1:%d", res), 838 "NOMAD_ADDR_REDIS": fmt.Sprintf("127.0.0.1:%d", dyn), 839 } 840 841 for key, val := range expectedEnvironment { 842 search := fmt.Sprintf("%s=%s", key, val) 843 if !inSlice(search, container.Config.Env) { 844 t.Errorf("Expected to find %s in container environment: %+v", search, container.Config.Env) 845 } 846 } 847 } 848 849 func TestDockerDriver_PortsMapping(t *testing.T) { 850 task, res, dyn := dockerTask() 851 task.Config["port_map"] = []map[string]string{ 852 map[string]string{ 853 "main": "8080", 854 "REDIS": "6379", 855 }, 856 } 857 858 client, handle, cleanup := dockerSetup(t, task) 859 defer cleanup() 860 861 waitForExist(t, client, handle.(*DockerHandle)) 862 863 container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID()) 864 if err != nil { 865 t.Fatalf("err: %v", err) 866 } 867 868 // Verify that the correct ports are EXPOSED 869 expectedExposedPorts := map[docker.Port]struct{}{ 870 docker.Port("8080/tcp"): struct{}{}, 871 docker.Port("8080/udp"): struct{}{}, 872 docker.Port("6379/tcp"): struct{}{}, 873 docker.Port("6379/udp"): struct{}{}, 874 } 875 876 if !reflect.DeepEqual(container.Config.ExposedPorts, expectedExposedPorts) { 877 t.Errorf("Exposed ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedExposedPorts, container.Config.ExposedPorts) 878 } 879 880 // Verify that the correct ports are FORWARDED 881 expectedPortBindings := map[docker.Port][]docker.PortBinding{ 882 docker.Port("8080/tcp"): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}}, 883 docker.Port("8080/udp"): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}}, 884 docker.Port("6379/tcp"): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}}, 885 docker.Port("6379/udp"): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}}, 886 } 887 888 if !reflect.DeepEqual(container.HostConfig.PortBindings, expectedPortBindings) { 889 t.Errorf("Forwarded ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedPortBindings, container.HostConfig.PortBindings) 890 } 891 892 expectedEnvironment := map[string]string{ 893 "NOMAD_ADDR_main": "127.0.0.1:8080", 894 "NOMAD_ADDR_REDIS": "127.0.0.1:6379", 895 "NOMAD_HOST_PORT_main": strconv.Itoa(docker_reserved), 896 } 897 898 for key, val := range expectedEnvironment { 899 search := fmt.Sprintf("%s=%s", key, val) 900 if !inSlice(search, container.Config.Env) { 901 t.Errorf("Expected to find %s in container environment: %+v", search, container.Config.Env) 902 } 903 } 904 } 905 906 func TestDockerDriver_User(t *testing.T) { 907 task := &structs.Task{ 908 Name: "redis-demo", 909 User: "alice", 910 Driver: "docker", 911 Config: map[string]interface{}{ 912 "image": "busybox", 913 "load": "busybox.tar", 914 "command": "/bin/sleep", 915 "args": []string{"10000"}, 916 }, 917 Resources: &structs.Resources{ 918 MemoryMB: 256, 919 CPU: 512, 920 }, 921 LogConfig: &structs.LogConfig{ 922 MaxFiles: 10, 923 MaxFileSizeMB: 10, 924 }, 925 } 926 927 if !testutil.DockerIsConnected(t) { 928 t.SkipNow() 929 } 930 931 ctx := testDockerDriverContexts(t, task) 932 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 933 driver := NewDockerDriver(ctx.DriverCtx) 934 defer ctx.AllocDir.Destroy() 935 copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar") 936 937 _, err := driver.Prestart(ctx.ExecCtx, task) 938 if err != nil { 939 t.Fatalf("error in prestart: %v", err) 940 } 941 942 // It should fail because the user "alice" does not exist on the given 943 // image. 944 handle, err := driver.Start(ctx.ExecCtx, task) 945 if err == nil { 946 handle.Kill() 947 t.Fatalf("Should've failed") 948 } 949 950 if !strings.Contains(err.Error(), "alice") { 951 t.Fatalf("Expected failure string not found, found %q instead", err.Error()) 952 } 953 } 954 955 func TestDockerDriver_CleanupContainer(t *testing.T) { 956 task := &structs.Task{ 957 Name: "redis-demo", 958 Driver: "docker", 959 Config: map[string]interface{}{ 960 "image": "busybox", 961 "load": "busybox.tar", 962 "command": "/bin/echo", 963 "args": []string{"hello"}, 964 }, 965 Resources: &structs.Resources{ 966 MemoryMB: 256, 967 CPU: 512, 968 }, 969 LogConfig: &structs.LogConfig{ 970 MaxFiles: 10, 971 MaxFileSizeMB: 10, 972 }, 973 } 974 975 _, handle, cleanup := dockerSetup(t, task) 976 defer cleanup() 977 978 // Update should be a no-op 979 err := handle.Update(task) 980 if err != nil { 981 t.Fatalf("err: %v", err) 982 } 983 984 select { 985 case res := <-handle.WaitCh(): 986 if !res.Successful() { 987 t.Fatalf("err: %v", res) 988 } 989 990 time.Sleep(3 * time.Second) 991 992 // Ensure that the container isn't present 993 _, err := client.InspectContainer(handle.(*DockerHandle).containerID) 994 if err == nil { 995 t.Fatalf("expected to not get container") 996 } 997 998 case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second): 999 t.Fatalf("timeout") 1000 } 1001 } 1002 1003 func TestDockerDriver_Stats(t *testing.T) { 1004 task := &structs.Task{ 1005 Name: "sleep", 1006 Driver: "docker", 1007 Config: map[string]interface{}{ 1008 "image": "busybox", 1009 "load": "busybox.tar", 1010 "command": "/bin/sleep", 1011 "args": []string{"100"}, 1012 }, 1013 LogConfig: &structs.LogConfig{ 1014 MaxFiles: 10, 1015 MaxFileSizeMB: 10, 1016 }, 1017 Resources: basicResources, 1018 } 1019 1020 _, handle, cleanup := dockerSetup(t, task) 1021 defer cleanup() 1022 1023 waitForExist(t, client, handle.(*DockerHandle)) 1024 1025 go func() { 1026 time.Sleep(3 * time.Second) 1027 ru, err := handle.Stats() 1028 if err != nil { 1029 t.Fatalf("err: %v", err) 1030 } 1031 if ru.ResourceUsage == nil { 1032 handle.Kill() 1033 t.Fatalf("expected resource usage") 1034 } 1035 err = handle.Kill() 1036 if err != nil { 1037 t.Fatalf("err: %v", err) 1038 } 1039 }() 1040 1041 select { 1042 case res := <-handle.WaitCh(): 1043 if res.Successful() { 1044 t.Fatalf("should err: %v", res) 1045 } 1046 case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second): 1047 t.Fatalf("timeout") 1048 } 1049 } 1050 1051 func TestDockerDriver_Signal(t *testing.T) { 1052 if !testutil.DockerIsConnected(t) { 1053 t.SkipNow() 1054 } 1055 1056 task := &structs.Task{ 1057 Name: "redis-demo", 1058 Driver: "docker", 1059 Config: map[string]interface{}{ 1060 "image": "busybox", 1061 "load": "busybox.tar", 1062 "command": "/bin/sh", 1063 "args": []string{"local/test.sh"}, 1064 }, 1065 Resources: &structs.Resources{ 1066 MemoryMB: 256, 1067 CPU: 512, 1068 }, 1069 LogConfig: &structs.LogConfig{ 1070 MaxFiles: 10, 1071 MaxFileSizeMB: 10, 1072 }, 1073 } 1074 1075 ctx := testDockerDriverContexts(t, task) 1076 //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} 1077 defer ctx.AllocDir.Destroy() 1078 d := NewDockerDriver(ctx.DriverCtx) 1079 1080 // Copy the image into the task's directory 1081 copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar") 1082 1083 testFile := filepath.Join(ctx.ExecCtx.TaskDir.LocalDir, "test.sh") 1084 testData := []byte(` 1085 at_term() { 1086 echo 'Terminated.' 1087 exit 3 1088 } 1089 trap at_term USR1 1090 while true; do 1091 sleep 1 1092 done 1093 `) 1094 if err := ioutil.WriteFile(testFile, testData, 0777); err != nil { 1095 t.Fatalf("Failed to write data: %v", err) 1096 } 1097 1098 _, err := d.Prestart(ctx.ExecCtx, task) 1099 if err != nil { 1100 t.Fatalf("error in prestart: %v", err) 1101 } 1102 handle, err := d.Start(ctx.ExecCtx, task) 1103 if err != nil { 1104 t.Fatalf("err: %v", err) 1105 } 1106 if handle == nil { 1107 t.Fatalf("missing handle") 1108 } 1109 defer handle.Kill() 1110 1111 waitForExist(t, handle.(*DockerHandle).client, handle.(*DockerHandle)) 1112 1113 time.Sleep(1 * time.Second) 1114 if err := handle.Signal(syscall.SIGUSR1); err != nil { 1115 t.Fatalf("Signal returned an error: %v", err) 1116 } 1117 1118 select { 1119 case res := <-handle.WaitCh(): 1120 if res.Successful() { 1121 t.Fatalf("should err: %v", res) 1122 } 1123 case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second): 1124 t.Fatalf("timeout") 1125 } 1126 1127 // Check the log file to see it exited because of the signal 1128 outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "redis-demo.stdout.0") 1129 act, err := ioutil.ReadFile(outputFile) 1130 if err != nil { 1131 t.Fatalf("Couldn't read expected output: %v", err) 1132 } 1133 1134 exp := "Terminated." 1135 if strings.TrimSpace(string(act)) != exp { 1136 t.Fatalf("Command outputted %v; want %v", act, exp) 1137 } 1138 } 1139 1140 func setupDockerVolumes(t *testing.T, cfg *config.Config, hostpath string) (*structs.Task, Driver, *ExecContext, string, func()) { 1141 if !testutil.DockerIsConnected(t) { 1142 t.SkipNow() 1143 } 1144 1145 randfn := fmt.Sprintf("test-%d", rand.Int()) 1146 hostfile := filepath.Join(hostpath, randfn) 1147 containerPath := "/mnt/vol" 1148 containerFile := filepath.Join(containerPath, randfn) 1149 1150 task := &structs.Task{ 1151 Name: "ls", 1152 Env: map[string]string{"VOL_PATH": containerPath}, 1153 Driver: "docker", 1154 Config: map[string]interface{}{ 1155 "image": "busybox", 1156 "load": "busybox.tar", 1157 "command": "touch", 1158 "args": []string{containerFile}, 1159 "volumes": []string{fmt.Sprintf("%s:${VOL_PATH}", hostpath)}, 1160 }, 1161 LogConfig: &structs.LogConfig{ 1162 MaxFiles: 10, 1163 MaxFileSizeMB: 10, 1164 }, 1165 Resources: basicResources, 1166 } 1167 1168 // Build alloc and task directory structure 1169 allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(cfg.AllocDir, structs.GenerateUUID())) 1170 if err := allocDir.Build(); err != nil { 1171 t.Fatalf("failed to build alloc dir: %v", err) 1172 } 1173 taskDir := allocDir.NewTaskDir(task.Name) 1174 if err := taskDir.Build(false, nil, cstructs.FSIsolationImage); err != nil { 1175 allocDir.Destroy() 1176 t.Fatalf("failed to build task dir: %v", err) 1177 } 1178 1179 alloc := mock.Alloc() 1180 execCtx := NewExecContext(taskDir) 1181 cleanup := func() { 1182 allocDir.Destroy() 1183 if filepath.IsAbs(hostpath) { 1184 os.RemoveAll(hostpath) 1185 } 1186 } 1187 1188 taskEnv, err := GetTaskEnv(taskDir, cfg.Node, task, alloc, cfg, "") 1189 if err != nil { 1190 cleanup() 1191 t.Fatalf("Failed to get task env: %v", err) 1192 } 1193 1194 logger := testLogger() 1195 emitter := func(m string, args ...interface{}) { 1196 logger.Printf("[EVENT] "+m, args...) 1197 } 1198 driverCtx := NewDriverContext(task.Name, alloc.ID, cfg, cfg.Node, testLogger(), taskEnv, emitter) 1199 driver := NewDockerDriver(driverCtx) 1200 copyImage(t, taskDir, "busybox.tar") 1201 1202 return task, driver, execCtx, hostfile, cleanup 1203 } 1204 1205 func TestDockerDriver_VolumesDisabled(t *testing.T) { 1206 cfg := testConfig() 1207 cfg.Options = map[string]string{ 1208 dockerVolumesConfigOption: "false", 1209 "docker.cleanup.image": "false", 1210 } 1211 1212 { 1213 tmpvol, err := ioutil.TempDir("", "nomadtest_docker_volumesdisabled") 1214 if err != nil { 1215 t.Fatalf("error creating temporary dir: %v", err) 1216 } 1217 1218 task, driver, execCtx, _, cleanup := setupDockerVolumes(t, cfg, tmpvol) 1219 defer cleanup() 1220 1221 _, err = driver.Prestart(execCtx, task) 1222 if err != nil { 1223 t.Fatalf("error in prestart: %v", err) 1224 } 1225 if _, err := driver.Start(execCtx, task); err == nil { 1226 t.Fatalf("Started driver successfully when volumes should have been disabled.") 1227 } 1228 } 1229 1230 // Relative paths should still be allowed 1231 { 1232 task, driver, execCtx, fn, cleanup := setupDockerVolumes(t, cfg, ".") 1233 defer cleanup() 1234 1235 _, err := driver.Prestart(execCtx, task) 1236 if err != nil { 1237 t.Fatalf("error in prestart: %v", err) 1238 } 1239 handle, err := driver.Start(execCtx, task) 1240 if err != nil { 1241 t.Fatalf("err: %v", err) 1242 } 1243 defer handle.Kill() 1244 1245 select { 1246 case res := <-handle.WaitCh(): 1247 if !res.Successful() { 1248 t.Fatalf("unexpected err: %v", res) 1249 } 1250 case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second): 1251 t.Fatalf("timeout") 1252 } 1253 1254 if _, err := ioutil.ReadFile(filepath.Join(execCtx.TaskDir.Dir, fn)); err != nil { 1255 t.Fatalf("unexpected error reading %s: %v", fn, err) 1256 } 1257 } 1258 1259 // Volume Drivers should be rejected (error) 1260 { 1261 task, driver, execCtx, _, cleanup := setupDockerVolumes(t, cfg, "fake_flocker_vol") 1262 defer cleanup() 1263 task.Config["volume_driver"] = "flocker" 1264 1265 if _, err := driver.Prestart(execCtx, task); err != nil { 1266 t.Fatalf("error in prestart: %v", err) 1267 } 1268 if _, err := driver.Start(execCtx, task); err == nil { 1269 t.Fatalf("Started driver successfully when volume drivers should have been disabled.") 1270 } 1271 } 1272 1273 } 1274 1275 func TestDockerDriver_VolumesEnabled(t *testing.T) { 1276 cfg := testConfig() 1277 1278 tmpvol, err := ioutil.TempDir("", "nomadtest_docker_volumesenabled") 1279 if err != nil { 1280 t.Fatalf("error creating temporary dir: %v", err) 1281 } 1282 1283 task, driver, execCtx, hostpath, cleanup := setupDockerVolumes(t, cfg, tmpvol) 1284 defer cleanup() 1285 1286 _, err = driver.Prestart(execCtx, task) 1287 if err != nil { 1288 t.Fatalf("error in prestart: %v", err) 1289 } 1290 handle, err := driver.Start(execCtx, task) 1291 if err != nil { 1292 t.Fatalf("Failed to start docker driver: %v", err) 1293 } 1294 defer handle.Kill() 1295 1296 select { 1297 case res := <-handle.WaitCh(): 1298 if !res.Successful() { 1299 t.Fatalf("unexpected err: %v", res) 1300 } 1301 case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second): 1302 t.Fatalf("timeout") 1303 } 1304 1305 if _, err := ioutil.ReadFile(hostpath); err != nil { 1306 t.Fatalf("unexpected error reading %s: %v", hostpath, err) 1307 } 1308 } 1309 1310 // TestDockerDriver_Cleanup ensures Cleanup removes only downloaded images. 1311 func TestDockerDriver_Cleanup(t *testing.T) { 1312 if !testutil.DockerIsConnected(t) { 1313 t.SkipNow() 1314 } 1315 1316 imageName := "hello-world:latest" 1317 task := &structs.Task{ 1318 Name: "cleanup_test", 1319 Driver: "docker", 1320 Config: map[string]interface{}{ 1321 "image": imageName, 1322 }, 1323 } 1324 tctx := testDockerDriverContexts(t, task) 1325 defer tctx.AllocDir.Destroy() 1326 1327 // Run Prestart 1328 driver := NewDockerDriver(tctx.DriverCtx).(*DockerDriver) 1329 res, err := driver.Prestart(tctx.ExecCtx, task) 1330 if err != nil { 1331 t.Fatalf("error in prestart: %v", err) 1332 } 1333 if len(res.Resources) == 0 || len(res.Resources[dockerImageResKey]) == 0 { 1334 t.Fatalf("no created resources: %#v", res) 1335 } 1336 1337 // Cleanup 1338 rescopy := res.Copy() 1339 if err := driver.Cleanup(tctx.ExecCtx, rescopy); err != nil { 1340 t.Fatalf("Cleanup failed: %v", err) 1341 } 1342 1343 // Make sure rescopy is updated 1344 if len(rescopy.Resources) > 0 { 1345 t.Errorf("Cleanup should have cleared resource map: %#v", rescopy.Resources) 1346 } 1347 1348 // Ensure image was removed 1349 tu.WaitForResult(func() (bool, error) { 1350 if _, err := client.InspectImage(driver.driverConfig.ImageName); err == nil { 1351 return false, fmt.Errorf("image exists but should have been removed. Does another %v container exist?", imageName) 1352 } 1353 1354 return true, nil 1355 }, func(err error) { 1356 t.Fatalf("err: %v", err) 1357 }) 1358 1359 // The image doesn't exist which shouldn't be an error when calling 1360 // Cleanup, so call it again to make sure. 1361 if err := driver.Cleanup(tctx.ExecCtx, res.Copy()); err != nil { 1362 t.Fatalf("Cleanup failed: %v", err) 1363 } 1364 } 1365 1366 func copyImage(t *testing.T, taskDir *allocdir.TaskDir, image string) { 1367 dst := filepath.Join(taskDir.LocalDir, image) 1368 copyFile(filepath.Join("./test-resources/docker", image), dst, t) 1369 } 1370 1371 func TestDockerDriver_AuthConfiguration(t *testing.T) { 1372 path := "./test-resources/docker/auth.json" 1373 cases := []struct { 1374 Repo string 1375 AuthConfig *docker.AuthConfiguration 1376 }{ 1377 { 1378 Repo: "lolwhat.com/what:1337", 1379 AuthConfig: &docker.AuthConfiguration{}, 1380 }, 1381 { 1382 Repo: "redis:3.2", 1383 AuthConfig: &docker.AuthConfiguration{ 1384 Username: "test", 1385 Password: "1234", 1386 Email: "", 1387 ServerAddress: "https://index.docker.io/v1/", 1388 }, 1389 }, 1390 { 1391 Repo: "quay.io/redis:3.2", 1392 AuthConfig: &docker.AuthConfiguration{ 1393 Username: "test", 1394 Password: "5678", 1395 Email: "", 1396 ServerAddress: "quay.io", 1397 }, 1398 }, 1399 { 1400 Repo: "other.io/redis:3.2", 1401 AuthConfig: &docker.AuthConfiguration{ 1402 Username: "test", 1403 Password: "abcd", 1404 Email: "", 1405 ServerAddress: "https://other.io/v1/", 1406 }, 1407 }, 1408 } 1409 1410 for i, c := range cases { 1411 act, err := authOptionFrom(path, c.Repo) 1412 if err != nil { 1413 t.Fatalf("Test %d failed: %v", i+1, err) 1414 } 1415 1416 if !reflect.DeepEqual(act, c.AuthConfig) { 1417 t.Fatalf("Test %d failed: Unexpected auth config: got %+v; want %+v", i+1, act, c.AuthConfig) 1418 } 1419 } 1420 }