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