github.com/emate/nomad@v0.8.2-wo-binpacking/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 }, 148 LogConfig: &structs.LogConfig{ 149 MaxFiles: 10, 150 MaxFileSizeMB: 10, 151 }, 152 Resources: &structs.Resources{ 153 MemoryMB: 128, 154 CPU: 100, 155 }, 156 } 157 158 ctx := testDriverContexts(t, task) 159 defer ctx.AllocDir.Destroy() 160 d := NewRktDriver(ctx.DriverCtx) 161 162 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 163 t.Fatalf("error in prestart: %v", err) 164 } 165 resp, err := d.Start(ctx.ExecCtx, task) 166 if err != nil { 167 t.Fatalf("err: %v", err) 168 } 169 handle := resp.Handle.(*rktHandle) 170 defer handle.Kill() 171 172 // Update should be a no-op 173 if err := handle.Update(task); err != nil { 174 t.Fatalf("err: %v", err) 175 } 176 177 // Signal should be an error 178 if err := resp.Handle.Signal(syscall.SIGTERM); err == nil { 179 t.Fatalf("err: %v", err) 180 } 181 182 select { 183 case res := <-resp.Handle.WaitCh(): 184 if !res.Successful() { 185 t.Fatalf("err: %v", res) 186 } 187 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 188 t.Fatalf("timeout") 189 } 190 191 // Make sure pod was removed #3561 192 var stderr bytes.Buffer 193 cmd := exec.Command(rktCmd, "status", handle.uuid) 194 cmd.Stdout = ioutil.Discard 195 cmd.Stderr = &stderr 196 if err := cmd.Run(); err == nil { 197 t.Fatalf("expected error running 'rkt status %s' on removed container", handle.uuid) 198 } 199 if out := stderr.String(); !strings.Contains(out, "no matches found") { 200 t.Fatalf("expected 'no matches found' but received: %s", out) 201 } 202 } 203 204 func TestRktDriver_Start_Wait_Skip_Trust(t *testing.T) { 205 ctestutil.RktCompatible(t) 206 if !testutil.IsTravis() { 207 t.Parallel() 208 } 209 210 task := &structs.Task{ 211 Name: "etcd", 212 Driver: "rkt", 213 Config: map[string]interface{}{ 214 "image": "coreos.com/etcd:v2.0.4", 215 "command": "/etcd", 216 "args": []string{"--version"}, 217 }, 218 LogConfig: &structs.LogConfig{ 219 MaxFiles: 10, 220 MaxFileSizeMB: 10, 221 }, 222 Resources: &structs.Resources{ 223 MemoryMB: 128, 224 CPU: 100, 225 }, 226 } 227 228 ctx := testDriverContexts(t, task) 229 defer ctx.AllocDir.Destroy() 230 d := NewRktDriver(ctx.DriverCtx) 231 232 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 233 t.Fatalf("error in prestart: %v", err) 234 } 235 resp, err := d.Start(ctx.ExecCtx, task) 236 if err != nil { 237 t.Fatalf("err: %v", err) 238 } 239 defer resp.Handle.Kill() 240 241 // Update should be a no-op 242 err = resp.Handle.Update(task) 243 if err != nil { 244 t.Fatalf("err: %v", err) 245 } 246 247 select { 248 case res := <-resp.Handle.WaitCh(): 249 if !res.Successful() { 250 t.Fatalf("err: %v", res) 251 } 252 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 253 t.Fatalf("timeout") 254 } 255 } 256 257 func TestRktDriver_Start_Wait_AllocDir(t *testing.T) { 258 ctestutil.RktCompatible(t) 259 if !testutil.IsTravis() { 260 t.Parallel() 261 } 262 263 exp := []byte{'w', 'i', 'n'} 264 file := "output.txt" 265 tmpvol, err := ioutil.TempDir("", "nomadtest_rktdriver_volumes") 266 if err != nil { 267 t.Fatalf("error creating temporary dir: %v", err) 268 } 269 defer os.RemoveAll(tmpvol) 270 hostpath := filepath.Join(tmpvol, file) 271 272 task := &structs.Task{ 273 Name: "rkttest_alpine", 274 Driver: "rkt", 275 Config: map[string]interface{}{ 276 "image": "docker://alpine", 277 "command": "/bin/sh", 278 "args": []string{ 279 "-c", 280 fmt.Sprintf(`echo -n %s > foo/%s`, string(exp), file), 281 }, 282 "net": []string{"none"}, 283 "volumes": []string{fmt.Sprintf("%s:/foo", tmpvol)}, 284 }, 285 LogConfig: &structs.LogConfig{ 286 MaxFiles: 10, 287 MaxFileSizeMB: 10, 288 }, 289 Resources: &structs.Resources{ 290 MemoryMB: 128, 291 CPU: 100, 292 }, 293 } 294 295 ctx := testDriverContexts(t, task) 296 defer ctx.AllocDir.Destroy() 297 d := NewRktDriver(ctx.DriverCtx) 298 299 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 300 t.Fatalf("error in prestart: %v", err) 301 } 302 resp, err := d.Start(ctx.ExecCtx, task) 303 if err != nil { 304 t.Fatalf("err: %v", err) 305 } 306 defer resp.Handle.Kill() 307 308 select { 309 case res := <-resp.Handle.WaitCh(): 310 if !res.Successful() { 311 t.Fatalf("err: %v", res) 312 } 313 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 314 t.Fatalf("timeout") 315 } 316 317 // Check that data was written to the shared alloc directory. 318 act, err := ioutil.ReadFile(hostpath) 319 if err != nil { 320 t.Fatalf("Couldn't read expected output: %v", err) 321 } 322 323 if !reflect.DeepEqual(act, exp) { 324 t.Fatalf("Command output is %v; expected %v", act, exp) 325 } 326 } 327 328 // TestRktDriver_UserGroup asserts tasks may override the user and group of the 329 // rkt image. 330 func TestRktDriver_UserGroup(t *testing.T) { 331 ctestutil.RktCompatible(t) 332 if !testutil.IsTravis() { 333 t.Parallel() 334 } 335 require := assert.New(t) 336 337 task := &structs.Task{ 338 Name: "etcd", 339 Driver: "rkt", 340 User: "nobody", 341 Config: map[string]interface{}{ 342 "image": "docker://redis:3.2", 343 "group": "nogroup", 344 }, 345 LogConfig: &structs.LogConfig{ 346 MaxFiles: 10, 347 MaxFileSizeMB: 10, 348 }, 349 Resources: &structs.Resources{ 350 MemoryMB: 128, 351 CPU: 100, 352 }, 353 } 354 355 tctx := testDriverContexts(t, task) 356 defer tctx.AllocDir.Destroy() 357 d := NewRktDriver(tctx.DriverCtx) 358 359 _, err := d.Prestart(tctx.ExecCtx, task) 360 require.Nil(err) 361 resp, err := d.Start(tctx.ExecCtx, task) 362 require.Nil(err) 363 defer resp.Handle.Kill() 364 365 timeout := time.Duration(testutil.TestMultiplier()*15) * time.Second 366 367 ctx, cancel := context.WithTimeout(context.Background(), timeout) 368 defer cancel() 369 370 // WaitUntil we can determine the user/group redis is running as 371 expected := []byte("redis-server *:6379 nobody nogroup\n") 372 testutil.WaitForResult(func() (bool, error) { 373 raw, code, err := resp.Handle.Exec(ctx, "/bin/bash", []string{"-c", "ps -eo args,user,group | grep ^redis"}) 374 if err != nil { 375 return false, err 376 } 377 if code != 0 { 378 return false, fmt.Errorf("unexpected exit code: %d", code) 379 } 380 return bytes.Equal(expected, raw), fmt.Errorf("expected %q but found %q", expected, raw) 381 }, func(err error) { 382 t.Fatalf("err: %v", err) 383 }) 384 385 require.Nil(resp.Handle.Kill()) 386 } 387 388 func TestRktTrustPrefix(t *testing.T) { 389 ctestutil.RktCompatible(t) 390 if !testutil.IsTravis() { 391 t.Parallel() 392 } 393 394 task := &structs.Task{ 395 Name: "etcd", 396 Driver: "rkt", 397 Config: map[string]interface{}{ 398 "trust_prefix": "example.com/invalid", 399 "image": "coreos.com/etcd:v2.0.4", 400 "command": "/etcd", 401 "args": []string{"--version"}, 402 }, 403 LogConfig: &structs.LogConfig{ 404 MaxFiles: 10, 405 MaxFileSizeMB: 10, 406 }, 407 Resources: &structs.Resources{ 408 MemoryMB: 128, 409 CPU: 100, 410 }, 411 } 412 ctx := testDriverContexts(t, task) 413 defer ctx.AllocDir.Destroy() 414 d := NewRktDriver(ctx.DriverCtx) 415 416 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 417 t.Fatalf("error in prestart: %v", err) 418 } 419 resp, err := d.Start(ctx.ExecCtx, task) 420 if err == nil { 421 resp.Handle.Kill() 422 t.Fatalf("Should've failed") 423 } 424 msg := "Error running rkt trust" 425 if !strings.Contains(err.Error(), msg) { 426 t.Fatalf("Expecting '%v' in '%v'", msg, err) 427 } 428 } 429 430 func TestRktTaskValidate(t *testing.T) { 431 ctestutil.RktCompatible(t) 432 t.Parallel() 433 434 task := &structs.Task{ 435 Name: "etcd", 436 Driver: "rkt", 437 Config: map[string]interface{}{ 438 "trust_prefix": "coreos.com/etcd", 439 "image": "coreos.com/etcd:v2.0.4", 440 "command": "/etcd", 441 "args": []string{"--version"}, 442 "dns_servers": []string{"8.8.8.8", "8.8.4.4"}, 443 "dns_search_domains": []string{"example.com", "example.org", "example.net"}, 444 }, 445 Resources: basicResources, 446 } 447 ctx := testDriverContexts(t, task) 448 defer ctx.AllocDir.Destroy() 449 d := NewRktDriver(ctx.DriverCtx) 450 451 if err := d.Validate(task.Config); err != nil { 452 t.Fatalf("Validation error in TaskConfig : '%v'", err) 453 } 454 } 455 456 func TestRktDriver_PortMapping(t *testing.T) { 457 ctestutil.RktCompatible(t) 458 if !testutil.IsTravis() { 459 t.Parallel() 460 } 461 462 task := &structs.Task{ 463 Name: "etcd", 464 Driver: "rkt", 465 Config: map[string]interface{}{ 466 "image": "docker://redis:3.2", 467 "port_map": []map[string]string{ 468 { 469 "main": "6379-tcp", 470 }, 471 }, 472 "debug": "true", 473 }, 474 LogConfig: &structs.LogConfig{ 475 MaxFiles: 10, 476 MaxFileSizeMB: 10, 477 }, 478 Resources: &structs.Resources{ 479 MemoryMB: 256, 480 CPU: 512, 481 Networks: []*structs.NetworkResource{ 482 { 483 IP: "127.0.0.1", 484 ReservedPorts: []structs.Port{{Label: "main", Value: 8080}}, 485 }, 486 }, 487 }, 488 } 489 490 ctx := testDriverContexts(t, task) 491 defer ctx.AllocDir.Destroy() 492 d := NewRktDriver(ctx.DriverCtx) 493 494 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 495 t.Fatalf("error in prestart: %v", err) 496 } 497 resp, err := d.Start(ctx.ExecCtx, task) 498 if err != nil { 499 t.Fatalf("err: %v", err) 500 } 501 defer resp.Handle.Kill() 502 if resp.Network == nil { 503 t.Fatalf("Expected driver to set a DriverNetwork, but it did not!") 504 } 505 506 failCh := make(chan error, 1) 507 go func() { 508 time.Sleep(1 * time.Second) 509 if err := resp.Handle.Kill(); err != nil { 510 failCh <- err 511 } 512 }() 513 514 select { 515 case err := <-failCh: 516 t.Fatalf("failed to kill handle: %v", err) 517 case <-resp.Handle.WaitCh(): 518 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 519 t.Fatalf("timeout") 520 } 521 } 522 523 // TestRktDriver_PortsMapping_Host asserts that port_map isn't required when 524 // host networking is used. 525 func TestRktDriver_PortsMapping_Host(t *testing.T) { 526 ctestutil.RktCompatible(t) 527 if !testutil.IsTravis() { 528 t.Parallel() 529 } 530 531 task := &structs.Task{ 532 Name: "etcd", 533 Driver: "rkt", 534 Config: map[string]interface{}{ 535 "image": "docker://redis:latest", 536 "net": []string{"host"}, 537 }, 538 LogConfig: &structs.LogConfig{ 539 MaxFiles: 10, 540 MaxFileSizeMB: 10, 541 }, 542 Resources: &structs.Resources{ 543 MemoryMB: 256, 544 CPU: 512, 545 Networks: []*structs.NetworkResource{ 546 { 547 IP: "127.0.0.1", 548 ReservedPorts: []structs.Port{{Label: "main", Value: 8080}}, 549 }, 550 }, 551 }, 552 } 553 554 ctx := testDriverContexts(t, task) 555 defer ctx.AllocDir.Destroy() 556 d := NewRktDriver(ctx.DriverCtx) 557 558 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 559 t.Fatalf("error in prestart: %v", err) 560 } 561 resp, err := d.Start(ctx.ExecCtx, task) 562 if err != nil { 563 t.Fatalf("err: %v", err) 564 } 565 defer resp.Handle.Kill() 566 if resp.Network != nil { 567 t.Fatalf("No network should be returned with --net=host but found: %#v", resp.Network) 568 } 569 570 failCh := make(chan error, 1) 571 go func() { 572 time.Sleep(1 * time.Second) 573 if err := resp.Handle.Kill(); err != nil { 574 failCh <- err 575 } 576 }() 577 578 select { 579 case err := <-failCh: 580 t.Fatalf("failed to kill handle: %v", err) 581 case <-resp.Handle.WaitCh(): 582 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 583 t.Fatalf("timeout") 584 } 585 } 586 587 func TestRktDriver_HandlerExec(t *testing.T) { 588 ctestutil.RktCompatible(t) 589 if !testutil.IsTravis() { 590 t.Parallel() 591 } 592 593 task := &structs.Task{ 594 Name: "etcd", 595 Driver: "rkt", 596 Config: map[string]interface{}{ 597 "trust_prefix": "coreos.com/etcd", 598 "image": "coreos.com/etcd:v2.0.4", 599 "command": "/etcd", 600 }, 601 LogConfig: &structs.LogConfig{ 602 MaxFiles: 10, 603 MaxFileSizeMB: 10, 604 }, 605 Resources: &structs.Resources{ 606 MemoryMB: 128, 607 CPU: 100, 608 }, 609 } 610 611 ctx := testDriverContexts(t, task) 612 defer ctx.AllocDir.Destroy() 613 d := NewRktDriver(ctx.DriverCtx) 614 615 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 616 t.Fatalf("error in prestart: %v", err) 617 } 618 resp, err := d.Start(ctx.ExecCtx, task) 619 if err != nil { 620 t.Fatalf("err: %v", err) 621 } 622 defer resp.Handle.Kill() 623 624 // Exec a command that should work 625 testutil.WaitForResult(func() (bool, error) { 626 out, code, err := resp.Handle.Exec(context.TODO(), "/etcd", []string{"--version"}) 627 if err != nil { 628 return false, fmt.Errorf("error exec'ing etcd --version: %v", err) 629 } 630 if code != 0 { 631 return false, fmt.Errorf("expected `etcd --version` to succeed but exit code was: %d\n%s", code, string(out)) 632 } 633 if expected := []byte("etcd version "); !bytes.HasPrefix(out, expected) { 634 return false, fmt.Errorf("expected output to start with %q but found:\n%q", expected, out) 635 } 636 637 return true, nil 638 }, func(err error) { 639 t.Fatalf("err: %v", err) 640 }) 641 642 // Exec a command that should fail 643 out, code, err := resp.Handle.Exec(context.TODO(), "/etcd", []string{"--kaljdshf"}) 644 if err != nil { 645 t.Fatalf("error exec'ing bad command: %v", err) 646 } 647 if code == 0 { 648 t.Fatalf("expected `stat` to fail but exit code was: %d", code) 649 } 650 if expected := "flag provided but not defined"; !bytes.Contains(out, []byte(expected)) { 651 t.Fatalf("expected output to contain %q but found: %q", expected, out) 652 } 653 654 if err := resp.Handle.Kill(); err != nil { 655 t.Fatalf("error killing handle: %v", err) 656 } 657 } 658 659 func TestRktDriver_Stats(t *testing.T) { 660 ctestutil.RktCompatible(t) 661 if !testutil.IsTravis() { 662 t.Parallel() 663 } 664 665 task := &structs.Task{ 666 Name: "etcd", 667 Driver: "rkt", 668 Config: map[string]interface{}{ 669 "trust_prefix": "coreos.com/etcd", 670 "image": "coreos.com/etcd:v2.0.4", 671 "command": "/etcd", 672 }, 673 LogConfig: &structs.LogConfig{ 674 MaxFiles: 10, 675 MaxFileSizeMB: 10, 676 }, 677 Resources: &structs.Resources{ 678 MemoryMB: 128, 679 CPU: 100, 680 }, 681 } 682 683 ctx := testDriverContexts(t, task) 684 defer ctx.AllocDir.Destroy() 685 d := NewRktDriver(ctx.DriverCtx) 686 687 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 688 t.Fatalf("error in prestart: %v", err) 689 } 690 resp, err := d.Start(ctx.ExecCtx, task) 691 if err != nil { 692 t.Fatalf("err: %v", err) 693 } 694 defer resp.Handle.Kill() 695 696 testutil.WaitForResult(func() (bool, error) { 697 stats, err := resp.Handle.Stats() 698 if err != nil { 699 return false, err 700 } 701 if stats == nil || stats.ResourceUsage == nil { 702 return false, fmt.Errorf("stats is nil") 703 } 704 if stats.ResourceUsage.CpuStats.TotalTicks == 0 { 705 return false, fmt.Errorf("cpu ticks unset") 706 } 707 if stats.ResourceUsage.MemoryStats.RSS == 0 { 708 return false, fmt.Errorf("rss stats unset") 709 } 710 return true, nil 711 }, func(err error) { 712 t.Fatalf("error: %v", err) 713 }) 714 715 } 716 717 func TestRktDriver_Remove_Error(t *testing.T) { 718 ctestutil.RktCompatible(t) 719 if !testutil.IsTravis() { 720 t.Parallel() 721 } 722 723 // Removing a nonexistent pod should return an error 724 if err := rktRemove("00000000-0000-0000-0000-000000000000"); err == nil { 725 t.Fatalf("expected an error") 726 } 727 728 if err := rktRemove("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); err == nil { 729 t.Fatalf("expected an error") 730 } 731 }