github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/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 func TestRktDriverUser(t *testing.T) { 348 assert := assert.New(t) 349 if !testutil.IsTravis() { 350 t.Parallel() 351 } 352 if os.Getenv("NOMAD_TEST_RKT") == "" { 353 t.Skip("skipping rkt tests") 354 } 355 356 ctestutils.RktCompatible(t) 357 task := &structs.Task{ 358 Name: "etcd", 359 Driver: "rkt", 360 User: "alice", 361 Config: map[string]interface{}{ 362 "trust_prefix": "coreos.com/etcd", 363 "image": "coreos.com/etcd:v2.0.4", 364 "command": "/etcd", 365 "args": []string{"--version"}, 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 ctx := testDriverContexts(t, task) 378 defer ctx.AllocDir.Destroy() 379 d := NewRktDriver(ctx.DriverCtx) 380 381 _, err := d.Prestart(ctx.ExecCtx, task) 382 assert.Nil(err) 383 resp, err := d.Start(ctx.ExecCtx, task) 384 assert.Nil(err) 385 defer resp.Handle.Kill() 386 387 select { 388 case res := <-resp.Handle.WaitCh(): 389 assert.False(res.Successful()) 390 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 391 t.Fatalf("timeout") 392 } 393 394 } 395 396 func TestRktTrustPrefix(t *testing.T) { 397 if !testutil.IsTravis() { 398 t.Parallel() 399 } 400 if os.Getenv("NOMAD_TEST_RKT") == "" { 401 t.Skip("skipping rkt tests") 402 } 403 ctestutils.RktCompatible(t) 404 task := &structs.Task{ 405 Name: "etcd", 406 Driver: "rkt", 407 Config: map[string]interface{}{ 408 "trust_prefix": "example.com/invalid", 409 "image": "coreos.com/etcd:v2.0.4", 410 "command": "/etcd", 411 "args": []string{"--version"}, 412 }, 413 LogConfig: &structs.LogConfig{ 414 MaxFiles: 10, 415 MaxFileSizeMB: 10, 416 }, 417 Resources: &structs.Resources{ 418 MemoryMB: 128, 419 CPU: 100, 420 }, 421 } 422 ctx := testDriverContexts(t, task) 423 defer ctx.AllocDir.Destroy() 424 d := NewRktDriver(ctx.DriverCtx) 425 426 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 427 t.Fatalf("error in prestart: %v", err) 428 } 429 resp, err := d.Start(ctx.ExecCtx, task) 430 if err == nil { 431 resp.Handle.Kill() 432 t.Fatalf("Should've failed") 433 } 434 msg := "Error running rkt trust" 435 if !strings.Contains(err.Error(), msg) { 436 t.Fatalf("Expecting '%v' in '%v'", msg, err) 437 } 438 } 439 440 func TestRktTaskValidate(t *testing.T) { 441 t.Parallel() 442 ctestutils.RktCompatible(t) 443 task := &structs.Task{ 444 Name: "etcd", 445 Driver: "rkt", 446 Config: map[string]interface{}{ 447 "trust_prefix": "coreos.com/etcd", 448 "image": "coreos.com/etcd:v2.0.4", 449 "command": "/etcd", 450 "args": []string{"--version"}, 451 "dns_servers": []string{"8.8.8.8", "8.8.4.4"}, 452 "dns_search_domains": []string{"example.com", "example.org", "example.net"}, 453 }, 454 Resources: basicResources, 455 } 456 ctx := testDriverContexts(t, task) 457 defer ctx.AllocDir.Destroy() 458 d := NewRktDriver(ctx.DriverCtx) 459 460 if err := d.Validate(task.Config); err != nil { 461 t.Fatalf("Validation error in TaskConfig : '%v'", err) 462 } 463 } 464 465 // TODO: Port Mapping test should be ran with proper ACI image and test the port access. 466 func TestRktDriver_PortsMapping(t *testing.T) { 467 if !testutil.IsTravis() { 468 t.Parallel() 469 } 470 if os.Getenv("NOMAD_TEST_RKT") == "" { 471 t.Skip("skipping rkt tests") 472 } 473 474 ctestutils.RktCompatible(t) 475 task := &structs.Task{ 476 Name: "etcd", 477 Driver: "rkt", 478 Config: map[string]interface{}{ 479 "image": "docker://redis:latest", 480 "port_map": []map[string]string{ 481 { 482 "main": "6379-tcp", 483 }, 484 }, 485 "debug": "true", 486 }, 487 LogConfig: &structs.LogConfig{ 488 MaxFiles: 10, 489 MaxFileSizeMB: 10, 490 }, 491 Resources: &structs.Resources{ 492 MemoryMB: 256, 493 CPU: 512, 494 Networks: []*structs.NetworkResource{ 495 { 496 IP: "127.0.0.1", 497 ReservedPorts: []structs.Port{{Label: "main", Value: 8080}}, 498 }, 499 }, 500 }, 501 } 502 503 ctx := testDriverContexts(t, task) 504 defer ctx.AllocDir.Destroy() 505 d := NewRktDriver(ctx.DriverCtx) 506 507 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 508 t.Fatalf("error in prestart: %v", err) 509 } 510 resp, err := d.Start(ctx.ExecCtx, task) 511 if err != nil { 512 t.Fatalf("err: %v", err) 513 } 514 if resp.Network == nil { 515 t.Fatalf("Expected driver to set a DriverNetwork, but it did not!") 516 } 517 518 failCh := make(chan error, 1) 519 go func() { 520 time.Sleep(1 * time.Second) 521 if err := resp.Handle.Kill(); err != nil { 522 failCh <- err 523 } 524 }() 525 526 select { 527 case err := <-failCh: 528 t.Fatalf("failed to kill handle: %v", err) 529 case <-resp.Handle.WaitCh(): 530 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 531 t.Fatalf("timeout") 532 } 533 } 534 535 // TestRktDriver_PortsMapping_Host asserts that port_map isn't required when 536 // host networking is used. 537 func TestRktDriver_PortsMapping_Host(t *testing.T) { 538 if !testutil.IsTravis() { 539 t.Parallel() 540 } 541 if os.Getenv("NOMAD_TEST_RKT") == "" { 542 t.Skip("skipping rkt tests") 543 } 544 545 ctestutils.RktCompatible(t) 546 task := &structs.Task{ 547 Name: "etcd", 548 Driver: "rkt", 549 Config: map[string]interface{}{ 550 "image": "docker://redis:latest", 551 "net": []string{"host"}, 552 }, 553 LogConfig: &structs.LogConfig{ 554 MaxFiles: 10, 555 MaxFileSizeMB: 10, 556 }, 557 Resources: &structs.Resources{ 558 MemoryMB: 256, 559 CPU: 512, 560 Networks: []*structs.NetworkResource{ 561 { 562 IP: "127.0.0.1", 563 ReservedPorts: []structs.Port{{Label: "main", Value: 8080}}, 564 }, 565 }, 566 }, 567 } 568 569 ctx := testDriverContexts(t, task) 570 defer ctx.AllocDir.Destroy() 571 d := NewRktDriver(ctx.DriverCtx) 572 573 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 574 t.Fatalf("error in prestart: %v", err) 575 } 576 resp, err := d.Start(ctx.ExecCtx, task) 577 if err != nil { 578 t.Fatalf("err: %v", err) 579 } 580 if resp.Network != nil { 581 t.Fatalf("No network should be returned with --net=host but found: %#v", resp.Network) 582 } 583 584 failCh := make(chan error, 1) 585 go func() { 586 time.Sleep(1 * time.Second) 587 if err := resp.Handle.Kill(); err != nil { 588 failCh <- err 589 } 590 }() 591 592 select { 593 case err := <-failCh: 594 t.Fatalf("failed to kill handle: %v", err) 595 case <-resp.Handle.WaitCh(): 596 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 597 t.Fatalf("timeout") 598 } 599 } 600 601 func TestRktDriver_HandlerExec(t *testing.T) { 602 if !testutil.IsTravis() { 603 t.Parallel() 604 } 605 if os.Getenv("NOMAD_TEST_RKT") == "" { 606 t.Skip("skipping rkt tests") 607 } 608 609 ctestutils.RktCompatible(t) 610 task := &structs.Task{ 611 Name: "etcd", 612 Driver: "rkt", 613 Config: map[string]interface{}{ 614 "trust_prefix": "coreos.com/etcd", 615 "image": "coreos.com/etcd:v2.0.4", 616 "command": "/etcd", 617 }, 618 LogConfig: &structs.LogConfig{ 619 MaxFiles: 10, 620 MaxFileSizeMB: 10, 621 }, 622 Resources: &structs.Resources{ 623 MemoryMB: 128, 624 CPU: 100, 625 }, 626 } 627 628 ctx := testDriverContexts(t, task) 629 defer ctx.AllocDir.Destroy() 630 d := NewRktDriver(ctx.DriverCtx) 631 632 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 633 t.Fatalf("error in prestart: %v", err) 634 } 635 resp, err := d.Start(ctx.ExecCtx, task) 636 if err != nil { 637 t.Fatalf("err: %v", err) 638 } 639 640 // Give the pod a second to start 641 time.Sleep(time.Second) 642 643 // Exec a command that should work 644 out, code, err := resp.Handle.Exec(context.TODO(), "/etcd", []string{"--version"}) 645 if err != nil { 646 t.Fatalf("error exec'ing etcd --version: %v", err) 647 } 648 if code != 0 { 649 t.Fatalf("expected `etcd --version` to succeed but exit code was: %d\n%s", code, string(out)) 650 } 651 if expected := []byte("etcd version "); !bytes.HasPrefix(out, expected) { 652 t.Fatalf("expected output to start with %q but found:\n%q", expected, out) 653 } 654 655 // Exec a command that should fail 656 out, code, err = resp.Handle.Exec(context.TODO(), "/etcd", []string{"--kaljdshf"}) 657 if err != nil { 658 t.Fatalf("error exec'ing bad command: %v", err) 659 } 660 if code == 0 { 661 t.Fatalf("expected `stat` to fail but exit code was: %d", code) 662 } 663 if expected := "flag provided but not defined"; !bytes.Contains(out, []byte(expected)) { 664 t.Fatalf("expected output to contain %q but found: %q", expected, out) 665 } 666 667 if err := resp.Handle.Kill(); err != nil { 668 t.Fatalf("error killing handle: %v", err) 669 } 670 } 671 672 func TestRktDriver_Remove_Error(t *testing.T) { 673 if !testutil.IsTravis() { 674 t.Parallel() 675 } 676 if os.Getenv("NOMAD_TEST_RKT") == "" { 677 t.Skip("skipping rkt tests") 678 } 679 680 ctestutils.RktCompatible(t) 681 682 // Removing a non-existent pod should return an error 683 if err := rktRemove("00000000-0000-0000-0000-000000000000"); err == nil { 684 t.Fatalf("expected an error") 685 } 686 687 if err := rktRemove("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); err == nil { 688 t.Fatalf("expected an error") 689 } 690 }