github.com/anuvu/nomad@v0.8.7-atom1/client/driver/rkt_test.go (about) 1 // +build linux 2 3 package driver 4 5 import ( 6 "bytes" 7 "context" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "reflect" 14 "strings" 15 "syscall" 16 "testing" 17 "time" 18 19 "github.com/hashicorp/nomad/client/config" 20 cstructs "github.com/hashicorp/nomad/client/structs" 21 "github.com/hashicorp/nomad/nomad/structs" 22 "github.com/hashicorp/nomad/testutil" 23 "github.com/stretchr/testify/assert" 24 25 ctestutil "github.com/hashicorp/nomad/client/testutil" 26 ) 27 28 func TestRktVersionRegex(t *testing.T) { 29 ctestutil.RktCompatible(t) 30 t.Parallel() 31 32 inputRkt := "rkt version 0.8.1" 33 inputAppc := "appc version 1.2.0" 34 expectedRkt := "0.8.1" 35 expectedAppc := "1.2.0" 36 rktMatches := reRktVersion.FindStringSubmatch(inputRkt) 37 appcMatches := reAppcVersion.FindStringSubmatch(inputAppc) 38 if rktMatches[1] != expectedRkt { 39 fmt.Printf("Test failed; got %q; want %q\n", rktMatches[1], expectedRkt) 40 } 41 if appcMatches[1] != expectedAppc { 42 fmt.Printf("Test failed; got %q; want %q\n", appcMatches[1], expectedAppc) 43 } 44 } 45 46 // The fingerprinter test should always pass, even if rkt is not installed. 47 func TestRktDriver_Fingerprint(t *testing.T) { 48 ctestutil.RktCompatible(t) 49 t.Parallel() 50 51 ctx := testDriverContexts(t, &structs.Task{Name: "foo", Driver: "rkt"}) 52 d := NewRktDriver(ctx.DriverCtx) 53 node := &structs.Node{ 54 Attributes: make(map[string]string), 55 } 56 57 request := &cstructs.FingerprintRequest{Config: &config.Config{}, Node: node} 58 var response cstructs.FingerprintResponse 59 err := d.Fingerprint(request, &response) 60 if err != nil { 61 t.Fatalf("err: %v", err) 62 } 63 64 if !response.Detected { 65 t.Fatalf("expected response to be applicable") 66 } 67 68 attributes := response.Attributes 69 if attributes == nil { 70 t.Fatalf("expected attributes to not equal nil") 71 } 72 if attributes["driver.rkt"] != "1" { 73 t.Fatalf("Missing Rkt driver") 74 } 75 if attributes["driver.rkt.version"] == "" { 76 t.Fatalf("Missing Rkt driver version") 77 } 78 if attributes["driver.rkt.appc.version"] == "" { 79 t.Fatalf("Missing appc version for the Rkt driver") 80 } 81 } 82 83 func TestRktDriver_Start_DNS(t *testing.T) { 84 ctestutil.RktCompatible(t) 85 if !testutil.IsTravis() { 86 t.Parallel() 87 } 88 89 task := &structs.Task{ 90 Name: "etcd", 91 Driver: "rkt", 92 Config: map[string]interface{}{ 93 "trust_prefix": "coreos.com/etcd", 94 "image": "coreos.com/etcd:v2.0.4", 95 "command": "/etcd", 96 "dns_servers": []string{"8.8.8.8", "8.8.4.4"}, 97 "dns_search_domains": []string{"example.com", "example.org", "example.net"}, 98 }, 99 LogConfig: &structs.LogConfig{ 100 MaxFiles: 10, 101 MaxFileSizeMB: 10, 102 }, 103 Resources: &structs.Resources{ 104 MemoryMB: 128, 105 CPU: 100, 106 }, 107 } 108 109 ctx := testDriverContexts(t, task) 110 defer ctx.AllocDir.Destroy() 111 d := NewRktDriver(ctx.DriverCtx) 112 113 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 114 t.Fatalf("error in prestart: %v", err) 115 } 116 resp, err := d.Start(ctx.ExecCtx, task) 117 if err != nil { 118 t.Fatalf("err: %v", err) 119 } 120 defer resp.Handle.Kill() 121 122 // Attempt to open 123 handle2, err := d.Open(ctx.ExecCtx, resp.Handle.ID()) 124 if err != nil { 125 t.Fatalf("err: %v", err) 126 } 127 if handle2 == nil { 128 t.Fatalf("missing handle") 129 } 130 handle2.Kill() 131 } 132 133 func TestRktDriver_Start_Wait(t *testing.T) { 134 ctestutil.RktCompatible(t) 135 if !testutil.IsTravis() { 136 t.Parallel() 137 } 138 139 task := &structs.Task{ 140 Name: "etcd", 141 Driver: "rkt", 142 Config: map[string]interface{}{ 143 "trust_prefix": "coreos.com/etcd", 144 "image": "coreos.com/etcd:v2.0.4", 145 "command": "/etcd", 146 "args": []string{"--version"}, 147 // Disable networking to speed up test as it's not needed 148 "net": []string{"none"}, 149 }, 150 LogConfig: &structs.LogConfig{ 151 MaxFiles: 10, 152 MaxFileSizeMB: 10, 153 }, 154 Resources: &structs.Resources{ 155 MemoryMB: 128, 156 CPU: 100, 157 }, 158 } 159 160 ctx := testDriverContexts(t, task) 161 defer ctx.AllocDir.Destroy() 162 d := NewRktDriver(ctx.DriverCtx) 163 164 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 165 t.Fatalf("error in prestart: %v", err) 166 } 167 resp, err := d.Start(ctx.ExecCtx, task) 168 if err != nil { 169 t.Fatalf("err: %v", err) 170 } 171 handle := resp.Handle.(*rktHandle) 172 defer handle.Kill() 173 174 // Update should be a no-op 175 if err := handle.Update(task); err != nil { 176 t.Fatalf("err: %v", err) 177 } 178 179 // Signal should be an error 180 if err := resp.Handle.Signal(syscall.SIGTERM); err == nil { 181 t.Fatalf("err: %v", err) 182 } 183 184 select { 185 case res := <-resp.Handle.WaitCh(): 186 if !res.Successful() { 187 t.Fatalf("err: %v", res) 188 } 189 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 190 t.Fatalf("timeout") 191 } 192 193 // Make sure pod was removed #3561 194 var stderr bytes.Buffer 195 cmd := exec.Command(rktCmd, "status", handle.uuid) 196 cmd.Stdout = ioutil.Discard 197 cmd.Stderr = &stderr 198 if err := cmd.Run(); err == nil { 199 t.Fatalf("expected error running 'rkt status %s' on removed container", handle.uuid) 200 } 201 if out := stderr.String(); !strings.Contains(out, "no matches found") { 202 t.Fatalf("expected 'no matches found' but received: %s", out) 203 } 204 } 205 206 func TestRktDriver_Start_Wait_Skip_Trust(t *testing.T) { 207 ctestutil.RktCompatible(t) 208 if !testutil.IsTravis() { 209 t.Parallel() 210 } 211 212 task := &structs.Task{ 213 Name: "etcd", 214 Driver: "rkt", 215 Config: map[string]interface{}{ 216 "image": "coreos.com/etcd:v2.0.4", 217 "command": "/etcd", 218 "args": []string{"--version"}, 219 // Disable networking to speed up test as it's not needed 220 "net": []string{"none"}, 221 }, 222 LogConfig: &structs.LogConfig{ 223 MaxFiles: 10, 224 MaxFileSizeMB: 10, 225 }, 226 Resources: &structs.Resources{ 227 MemoryMB: 128, 228 CPU: 100, 229 }, 230 } 231 232 ctx := testDriverContexts(t, task) 233 defer ctx.AllocDir.Destroy() 234 d := NewRktDriver(ctx.DriverCtx) 235 236 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 237 t.Fatalf("error in prestart: %v", err) 238 } 239 resp, err := d.Start(ctx.ExecCtx, task) 240 if err != nil { 241 t.Fatalf("err: %v", err) 242 } 243 defer resp.Handle.Kill() 244 245 // Update should be a no-op 246 err = resp.Handle.Update(task) 247 if err != nil { 248 t.Fatalf("err: %v", err) 249 } 250 251 select { 252 case res := <-resp.Handle.WaitCh(): 253 if !res.Successful() { 254 t.Fatalf("err: %v", res) 255 } 256 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 257 t.Fatalf("timeout") 258 } 259 } 260 261 func TestRktDriver_Start_Wait_AllocDir(t *testing.T) { 262 ctestutil.RktCompatible(t) 263 if !testutil.IsTravis() { 264 t.Parallel() 265 } 266 267 exp := []byte{'w', 'i', 'n'} 268 file := "output.txt" 269 tmpvol, err := ioutil.TempDir("", "nomadtest_rktdriver_volumes") 270 if err != nil { 271 t.Fatalf("error creating temporary dir: %v", err) 272 } 273 defer os.RemoveAll(tmpvol) 274 hostpath := filepath.Join(tmpvol, file) 275 276 task := &structs.Task{ 277 Name: "rkttest_alpine", 278 Driver: "rkt", 279 Config: map[string]interface{}{ 280 "image": "docker://redis:3.2-alpine", 281 "command": "/bin/sh", 282 "args": []string{ 283 "-c", 284 fmt.Sprintf("echo -n %s > /foo/%s", string(exp), file), 285 }, 286 "net": []string{"none"}, 287 "volumes": []string{fmt.Sprintf("%s:/foo", tmpvol)}, 288 }, 289 LogConfig: &structs.LogConfig{ 290 MaxFiles: 10, 291 MaxFileSizeMB: 10, 292 }, 293 Resources: &structs.Resources{ 294 MemoryMB: 128, 295 CPU: 100, 296 }, 297 } 298 299 ctx := testDriverContexts(t, task) 300 defer ctx.AllocDir.Destroy() 301 d := NewRktDriver(ctx.DriverCtx) 302 303 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 304 t.Fatalf("error in prestart: %v", err) 305 } 306 resp, err := d.Start(ctx.ExecCtx, task) 307 if err != nil { 308 t.Fatalf("err: %v", err) 309 } 310 defer resp.Handle.Kill() 311 312 select { 313 case res := <-resp.Handle.WaitCh(): 314 if !res.Successful() { 315 t.Fatalf("err: %v", res) 316 } 317 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 318 t.Fatalf("timeout") 319 } 320 321 // Check that data was written to the shared alloc directory. 322 act, err := ioutil.ReadFile(hostpath) 323 if err != nil { 324 t.Fatalf("Couldn't read expected output: %v", err) 325 } 326 327 if !reflect.DeepEqual(act, exp) { 328 t.Fatalf("Command output is %v; expected %v", act, exp) 329 } 330 } 331 332 // TestRktDriver_UserGroup asserts tasks may override the user and group of the 333 // rkt image. 334 func TestRktDriver_UserGroup(t *testing.T) { 335 ctestutil.RktCompatible(t) 336 if !testutil.IsTravis() { 337 t.Parallel() 338 } 339 require := assert.New(t) 340 341 task := &structs.Task{ 342 Name: "sleepy", 343 Driver: "rkt", 344 User: "nobody", 345 Config: map[string]interface{}{ 346 "image": "docker://redis:3.2-alpine", 347 "group": "nogroup", 348 "command": "sleep", 349 "args": []string{"9000"}, 350 }, 351 LogConfig: &structs.LogConfig{ 352 MaxFiles: 10, 353 MaxFileSizeMB: 10, 354 }, 355 Resources: &structs.Resources{ 356 MemoryMB: 128, 357 CPU: 100, 358 }, 359 } 360 361 tctx := testDriverContexts(t, task) 362 defer tctx.AllocDir.Destroy() 363 d := NewRktDriver(tctx.DriverCtx) 364 365 _, err := d.Prestart(tctx.ExecCtx, task) 366 require.NoError(err) 367 resp, err := d.Start(tctx.ExecCtx, task) 368 require.NoError(err) 369 defer resp.Handle.Kill() 370 371 ctx := context.Background() 372 373 // WaitUntil we can determine the user/group redis is running as 374 expected := []byte("\nnobody nogroup /bin/sleep 9000\n") 375 testutil.WaitForResult(func() (bool, error) { 376 raw, code, err := resp.Handle.Exec(ctx, "ps", []string{"-o", "user,group,args"}) 377 if err != nil { 378 err = fmt.Errorf("original error: %v; code: %d; raw output: %s", err, code, string(raw)) 379 return false, err 380 } 381 if code != 0 { 382 return false, fmt.Errorf("unexpected exit code: %d", code) 383 } 384 return bytes.Contains(raw, expected), fmt.Errorf("expected %q but found:\n%s", expected, raw) 385 }, func(err error) { 386 t.Fatalf("err: %v", err) 387 }) 388 389 require.NoError(resp.Handle.Kill()) 390 } 391 392 func TestRktTrustPrefix(t *testing.T) { 393 ctestutil.RktCompatible(t) 394 if !testutil.IsTravis() { 395 t.Parallel() 396 } 397 398 task := &structs.Task{ 399 Name: "etcd", 400 Driver: "rkt", 401 Config: map[string]interface{}{ 402 "trust_prefix": "example.com/invalid", 403 "image": "coreos.com/etcd:v2.0.4", 404 "command": "/etcd", 405 "args": []string{"--version"}, 406 }, 407 LogConfig: &structs.LogConfig{ 408 MaxFiles: 10, 409 MaxFileSizeMB: 10, 410 }, 411 Resources: &structs.Resources{ 412 MemoryMB: 128, 413 CPU: 100, 414 }, 415 } 416 ctx := testDriverContexts(t, task) 417 defer ctx.AllocDir.Destroy() 418 d := NewRktDriver(ctx.DriverCtx) 419 420 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 421 t.Fatalf("error in prestart: %v", err) 422 } 423 resp, err := d.Start(ctx.ExecCtx, task) 424 if err == nil { 425 resp.Handle.Kill() 426 t.Fatalf("Should've failed") 427 } 428 msg := "Error running rkt trust" 429 if !strings.Contains(err.Error(), msg) { 430 t.Fatalf("Expecting '%v' in '%v'", msg, err) 431 } 432 } 433 434 func TestRktTaskValidate(t *testing.T) { 435 ctestutil.RktCompatible(t) 436 t.Parallel() 437 438 task := &structs.Task{ 439 Name: "etcd", 440 Driver: "rkt", 441 Config: map[string]interface{}{ 442 "trust_prefix": "coreos.com/etcd", 443 "image": "coreos.com/etcd:v2.0.4", 444 "command": "/etcd", 445 "args": []string{"--version"}, 446 "dns_servers": []string{"8.8.8.8", "8.8.4.4"}, 447 "dns_search_domains": []string{"example.com", "example.org", "example.net"}, 448 }, 449 Resources: basicResources, 450 } 451 ctx := testDriverContexts(t, task) 452 defer ctx.AllocDir.Destroy() 453 d := NewRktDriver(ctx.DriverCtx) 454 455 if err := d.Validate(task.Config); err != nil { 456 t.Fatalf("Validation error in TaskConfig : '%v'", err) 457 } 458 } 459 460 func TestRktDriver_PortMapping(t *testing.T) { 461 ctestutil.RktCompatible(t) 462 if !testutil.IsTravis() { 463 t.Parallel() 464 } 465 466 task := &structs.Task{ 467 Name: "redis", 468 Driver: "rkt", 469 Config: map[string]interface{}{ 470 "image": "docker://redis:3.2-alpine", 471 "port_map": []map[string]string{ 472 { 473 "main": "6379-tcp", 474 }, 475 }, 476 "debug": "true", 477 }, 478 LogConfig: &structs.LogConfig{ 479 MaxFiles: 10, 480 MaxFileSizeMB: 10, 481 }, 482 Resources: &structs.Resources{ 483 MemoryMB: 256, 484 CPU: 512, 485 Networks: []*structs.NetworkResource{ 486 { 487 IP: "127.0.0.1", 488 ReservedPorts: []structs.Port{{Label: "main", Value: 8080}}, 489 }, 490 }, 491 }, 492 } 493 494 ctx := testDriverContexts(t, task) 495 defer ctx.AllocDir.Destroy() 496 d := NewRktDriver(ctx.DriverCtx) 497 498 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 499 t.Fatalf("error in prestart: %v", err) 500 } 501 resp, err := d.Start(ctx.ExecCtx, task) 502 if err != nil { 503 t.Fatalf("err: %v", err) 504 } 505 defer resp.Handle.Kill() 506 if resp.Network == nil { 507 t.Fatalf("Expected driver to set a DriverNetwork, but it did not!") 508 } 509 510 failCh := make(chan error, 1) 511 go func() { 512 time.Sleep(1 * time.Second) 513 if err := resp.Handle.Kill(); err != nil { 514 failCh <- err 515 } 516 }() 517 518 select { 519 case err := <-failCh: 520 t.Fatalf("failed to kill handle: %v", err) 521 case <-resp.Handle.WaitCh(): 522 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 523 t.Fatalf("timeout") 524 } 525 } 526 527 // TestRktDriver_PortsMapping_Host asserts that port_map isn't required when 528 // host networking is used. 529 func TestRktDriver_PortsMapping_Host(t *testing.T) { 530 ctestutil.RktCompatible(t) 531 if !testutil.IsTravis() { 532 t.Parallel() 533 } 534 535 task := &structs.Task{ 536 Name: "redis", 537 Driver: "rkt", 538 Config: map[string]interface{}{ 539 "image": "docker://redis:3.2-alpine", 540 "net": []string{"host"}, 541 }, 542 LogConfig: &structs.LogConfig{ 543 MaxFiles: 10, 544 MaxFileSizeMB: 10, 545 }, 546 Resources: &structs.Resources{ 547 MemoryMB: 256, 548 CPU: 512, 549 Networks: []*structs.NetworkResource{ 550 { 551 IP: "127.0.0.1", 552 ReservedPorts: []structs.Port{{Label: "main", Value: 8080}}, 553 }, 554 }, 555 }, 556 } 557 558 ctx := testDriverContexts(t, task) 559 defer ctx.AllocDir.Destroy() 560 d := NewRktDriver(ctx.DriverCtx) 561 562 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 563 t.Fatalf("error in prestart: %v", err) 564 } 565 resp, err := d.Start(ctx.ExecCtx, task) 566 if err != nil { 567 t.Fatalf("err: %v", err) 568 } 569 defer resp.Handle.Kill() 570 if resp.Network != nil { 571 t.Fatalf("No network should be returned with --net=host but found: %#v", resp.Network) 572 } 573 574 failCh := make(chan error, 1) 575 go func() { 576 time.Sleep(1 * time.Second) 577 if err := resp.Handle.Kill(); err != nil { 578 failCh <- err 579 } 580 }() 581 582 select { 583 case err := <-failCh: 584 t.Fatalf("failed to kill handle: %v", err) 585 case <-resp.Handle.WaitCh(): 586 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 587 t.Fatalf("timeout") 588 } 589 } 590 591 func TestRktDriver_HandlerExec(t *testing.T) { 592 ctestutil.RktCompatible(t) 593 if !testutil.IsTravis() { 594 t.Parallel() 595 } 596 597 task := &structs.Task{ 598 Name: "etcd", 599 Driver: "rkt", 600 Config: map[string]interface{}{ 601 "trust_prefix": "coreos.com/etcd", 602 "image": "coreos.com/etcd:v2.0.4", 603 "command": "/etcd", 604 }, 605 LogConfig: &structs.LogConfig{ 606 MaxFiles: 10, 607 MaxFileSizeMB: 10, 608 }, 609 Resources: &structs.Resources{ 610 MemoryMB: 128, 611 CPU: 100, 612 }, 613 } 614 615 ctx := testDriverContexts(t, task) 616 defer ctx.AllocDir.Destroy() 617 d := NewRktDriver(ctx.DriverCtx) 618 619 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 620 t.Fatalf("error in prestart: %v", err) 621 } 622 resp, err := d.Start(ctx.ExecCtx, task) 623 if err != nil { 624 t.Fatalf("err: %v", err) 625 } 626 defer resp.Handle.Kill() 627 628 // Exec a command that should work 629 testutil.WaitForResult(func() (bool, error) { 630 out, code, err := resp.Handle.Exec(context.TODO(), "/etcd", []string{"--version"}) 631 if err != nil { 632 return false, fmt.Errorf("error exec'ing etcd --version: %v", err) 633 } 634 if code != 0 { 635 return false, fmt.Errorf("expected `etcd --version` to succeed but exit code was: %d\n%s", code, string(out)) 636 } 637 if expected := []byte("etcd version "); !bytes.HasPrefix(out, expected) { 638 return false, fmt.Errorf("expected output to start with %q but found:\n%q", expected, out) 639 } 640 641 return true, nil 642 }, func(err error) { 643 t.Fatalf("err: %v", err) 644 }) 645 646 // Exec a command that should fail 647 out, code, err := resp.Handle.Exec(context.TODO(), "/etcd", []string{"--kaljdshf"}) 648 if err != nil { 649 t.Fatalf("error exec'ing bad command: %v", err) 650 } 651 if code == 0 { 652 t.Fatalf("expected `stat` to fail but exit code was: %d", code) 653 } 654 if expected := "flag provided but not defined"; !bytes.Contains(out, []byte(expected)) { 655 t.Fatalf("expected output to contain %q but found: %q", expected, out) 656 } 657 658 if err := resp.Handle.Kill(); err != nil { 659 t.Fatalf("error killing handle: %v", err) 660 } 661 } 662 663 func TestRktDriver_Stats(t *testing.T) { 664 ctestutil.RktCompatible(t) 665 if !testutil.IsTravis() { 666 t.Parallel() 667 } 668 669 task := &structs.Task{ 670 Name: "etcd", 671 Driver: "rkt", 672 Config: map[string]interface{}{ 673 "trust_prefix": "coreos.com/etcd", 674 "image": "coreos.com/etcd:v2.0.4", 675 "command": "/etcd", 676 }, 677 LogConfig: &structs.LogConfig{ 678 MaxFiles: 10, 679 MaxFileSizeMB: 10, 680 }, 681 Resources: &structs.Resources{ 682 MemoryMB: 128, 683 CPU: 100, 684 }, 685 } 686 687 ctx := testDriverContexts(t, task) 688 defer ctx.AllocDir.Destroy() 689 d := NewRktDriver(ctx.DriverCtx) 690 691 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 692 t.Fatalf("error in prestart: %v", err) 693 } 694 resp, err := d.Start(ctx.ExecCtx, task) 695 if err != nil { 696 t.Fatalf("err: %v", err) 697 } 698 defer resp.Handle.Kill() 699 700 testutil.WaitForResult(func() (bool, error) { 701 stats, err := resp.Handle.Stats() 702 if err != nil { 703 return false, err 704 } 705 if stats == nil || stats.ResourceUsage == nil { 706 return false, fmt.Errorf("stats is nil") 707 } 708 if stats.ResourceUsage.CpuStats.TotalTicks == 0 { 709 return false, fmt.Errorf("cpu ticks unset") 710 } 711 if stats.ResourceUsage.MemoryStats.RSS == 0 { 712 return false, fmt.Errorf("rss stats unset") 713 } 714 return true, nil 715 }, func(err error) { 716 t.Fatalf("error: %v", err) 717 }) 718 719 } 720 721 func TestRktDriver_Remove_Error(t *testing.T) { 722 ctestutil.RktCompatible(t) 723 if !testutil.IsTravis() { 724 t.Parallel() 725 } 726 727 // Removing a nonexistent pod should return an error 728 if err := rktRemove("00000000-0000-0000-0000-000000000000"); err == nil { 729 t.Fatalf("expected an error") 730 } 731 732 if err := rktRemove("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); err == nil { 733 t.Fatalf("expected an error") 734 } 735 }