github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/api/jobs_test.go (about) 1 package api 2 3 import ( 4 "reflect" 5 "sort" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/ncodes/nomad/helper" 11 "github.com/ncodes/nomad/testutil" 12 ) 13 14 func TestJobs_Register(t *testing.T) { 15 c, s := makeClient(t, nil, nil) 16 defer s.Stop() 17 jobs := c.Jobs() 18 19 // Listing jobs before registering returns nothing 20 resp, qm, err := jobs.List(nil) 21 if err != nil { 22 t.Fatalf("err: %s", err) 23 } 24 if qm.LastIndex != 0 { 25 t.Fatalf("bad index: %d", qm.LastIndex) 26 } 27 if n := len(resp); n != 0 { 28 t.Fatalf("expected 0 jobs, got: %d", n) 29 } 30 31 // Create a job and attempt to register it 32 job := testJob() 33 eval, wm, err := jobs.Register(job, nil) 34 if err != nil { 35 t.Fatalf("err: %s", err) 36 } 37 if eval == "" { 38 t.Fatalf("missing eval id") 39 } 40 assertWriteMeta(t, wm) 41 42 // Query the jobs back out again 43 resp, qm, err = jobs.List(nil) 44 if err != nil { 45 t.Fatalf("err: %s", err) 46 } 47 assertQueryMeta(t, qm) 48 49 // Check that we got the expected response 50 if len(resp) != 1 || resp[0].ID != *job.ID { 51 t.Fatalf("bad: %#v", resp[0]) 52 } 53 } 54 55 func TestJobs_Validate(t *testing.T) { 56 c, s := makeClient(t, nil, nil) 57 defer s.Stop() 58 jobs := c.Jobs() 59 60 // Create a job and attempt to register it 61 job := testJob() 62 resp, _, err := jobs.Validate(job, nil) 63 if err != nil { 64 t.Fatalf("err: %s", err) 65 } 66 67 if len(resp.ValidationErrors) != 0 { 68 t.Fatalf("bad %v", resp) 69 } 70 71 job.ID = nil 72 resp1, _, err := jobs.Validate(job, nil) 73 if err != nil { 74 t.Fatalf("err: %v", err) 75 } 76 77 if len(resp1.ValidationErrors) == 0 { 78 t.Fatalf("bad %v", resp1) 79 } 80 } 81 82 func TestJobs_Canonicalize(t *testing.T) { 83 testCases := []struct { 84 name string 85 expected *Job 86 input *Job 87 }{ 88 { 89 name: "empty", 90 input: &Job{ 91 TaskGroups: []*TaskGroup{ 92 { 93 Tasks: []*Task{ 94 {}, 95 }, 96 }, 97 }, 98 }, 99 expected: &Job{ 100 ID: helper.StringToPtr(""), 101 Name: helper.StringToPtr(""), 102 Region: helper.StringToPtr("global"), 103 Type: helper.StringToPtr("service"), 104 ParentID: helper.StringToPtr(""), 105 Priority: helper.IntToPtr(50), 106 AllAtOnce: helper.BoolToPtr(false), 107 VaultToken: helper.StringToPtr(""), 108 Status: helper.StringToPtr(""), 109 StatusDescription: helper.StringToPtr(""), 110 CreateIndex: helper.Uint64ToPtr(0), 111 ModifyIndex: helper.Uint64ToPtr(0), 112 JobModifyIndex: helper.Uint64ToPtr(0), 113 TaskGroups: []*TaskGroup{ 114 { 115 Name: helper.StringToPtr(""), 116 Count: helper.IntToPtr(1), 117 EphemeralDisk: &EphemeralDisk{ 118 Sticky: helper.BoolToPtr(false), 119 Migrate: helper.BoolToPtr(false), 120 SizeMB: helper.IntToPtr(300), 121 }, 122 RestartPolicy: &RestartPolicy{ 123 Delay: helper.TimeToPtr(15 * time.Second), 124 Attempts: helper.IntToPtr(2), 125 Interval: helper.TimeToPtr(1 * time.Minute), 126 Mode: helper.StringToPtr("delay"), 127 }, 128 Tasks: []*Task{ 129 { 130 KillTimeout: helper.TimeToPtr(5 * time.Second), 131 LogConfig: DefaultLogConfig(), 132 Resources: MinResources(), 133 }, 134 }, 135 }, 136 }, 137 }, 138 }, 139 { 140 name: "partial", 141 input: &Job{ 142 Name: helper.StringToPtr("foo"), 143 ID: helper.StringToPtr("bar"), 144 ParentID: helper.StringToPtr("lol"), 145 TaskGroups: []*TaskGroup{ 146 { 147 Name: helper.StringToPtr("bar"), 148 Tasks: []*Task{ 149 { 150 Name: "task1", 151 }, 152 }, 153 }, 154 }, 155 }, 156 expected: &Job{ 157 ID: helper.StringToPtr("bar"), 158 Name: helper.StringToPtr("foo"), 159 Region: helper.StringToPtr("global"), 160 Type: helper.StringToPtr("service"), 161 ParentID: helper.StringToPtr("lol"), 162 Priority: helper.IntToPtr(50), 163 AllAtOnce: helper.BoolToPtr(false), 164 VaultToken: helper.StringToPtr(""), 165 Status: helper.StringToPtr(""), 166 StatusDescription: helper.StringToPtr(""), 167 CreateIndex: helper.Uint64ToPtr(0), 168 ModifyIndex: helper.Uint64ToPtr(0), 169 JobModifyIndex: helper.Uint64ToPtr(0), 170 TaskGroups: []*TaskGroup{ 171 { 172 Name: helper.StringToPtr("bar"), 173 Count: helper.IntToPtr(1), 174 EphemeralDisk: &EphemeralDisk{ 175 Sticky: helper.BoolToPtr(false), 176 Migrate: helper.BoolToPtr(false), 177 SizeMB: helper.IntToPtr(300), 178 }, 179 RestartPolicy: &RestartPolicy{ 180 Delay: helper.TimeToPtr(15 * time.Second), 181 Attempts: helper.IntToPtr(2), 182 Interval: helper.TimeToPtr(1 * time.Minute), 183 Mode: helper.StringToPtr("delay"), 184 }, 185 Tasks: []*Task{ 186 { 187 Name: "task1", 188 LogConfig: DefaultLogConfig(), 189 Resources: MinResources(), 190 KillTimeout: helper.TimeToPtr(5 * time.Second), 191 }, 192 }, 193 }, 194 }, 195 }, 196 }, 197 { 198 name: "example_template", 199 input: &Job{ 200 ID: helper.StringToPtr("example_template"), 201 Name: helper.StringToPtr("example_template"), 202 Datacenters: []string{"dc1"}, 203 Type: helper.StringToPtr("service"), 204 Update: &UpdateStrategy{ 205 Stagger: 10 * time.Second, 206 MaxParallel: 1, 207 }, 208 TaskGroups: []*TaskGroup{ 209 { 210 Name: helper.StringToPtr("cache"), 211 Count: helper.IntToPtr(1), 212 RestartPolicy: &RestartPolicy{ 213 Interval: helper.TimeToPtr(5 * time.Minute), 214 Attempts: helper.IntToPtr(10), 215 Delay: helper.TimeToPtr(25 * time.Second), 216 Mode: helper.StringToPtr("delay"), 217 }, 218 EphemeralDisk: &EphemeralDisk{ 219 SizeMB: helper.IntToPtr(300), 220 }, 221 Tasks: []*Task{ 222 { 223 Name: "redis", 224 Driver: "docker", 225 Config: map[string]interface{}{ 226 "image": "redis:3.2", 227 "port_map": map[string]int{ 228 "db": 6379, 229 }, 230 }, 231 Resources: &Resources{ 232 CPU: helper.IntToPtr(500), 233 MemoryMB: helper.IntToPtr(256), 234 Networks: []*NetworkResource{ 235 { 236 MBits: helper.IntToPtr(10), 237 DynamicPorts: []Port{ 238 { 239 Label: "db", 240 }, 241 }, 242 }, 243 }, 244 }, 245 Services: []*Service{ 246 { 247 Name: "global-redis-check", 248 Tags: []string{"global", "cache"}, 249 PortLabel: "db", 250 Checks: []ServiceCheck{ 251 { 252 Name: "alive", 253 Type: "tcp", 254 Interval: 10 * time.Second, 255 Timeout: 2 * time.Second, 256 }, 257 }, 258 }, 259 }, 260 Templates: []*Template{ 261 { 262 EmbeddedTmpl: helper.StringToPtr("---"), 263 DestPath: helper.StringToPtr("local/file.yml"), 264 }, 265 }, 266 }, 267 }, 268 }, 269 }, 270 }, 271 expected: &Job{ 272 ID: helper.StringToPtr("example_template"), 273 Name: helper.StringToPtr("example_template"), 274 ParentID: helper.StringToPtr(""), 275 Priority: helper.IntToPtr(50), 276 Region: helper.StringToPtr("global"), 277 Type: helper.StringToPtr("service"), 278 AllAtOnce: helper.BoolToPtr(false), 279 VaultToken: helper.StringToPtr(""), 280 Status: helper.StringToPtr(""), 281 StatusDescription: helper.StringToPtr(""), 282 CreateIndex: helper.Uint64ToPtr(0), 283 ModifyIndex: helper.Uint64ToPtr(0), 284 JobModifyIndex: helper.Uint64ToPtr(0), 285 Datacenters: []string{"dc1"}, 286 Update: &UpdateStrategy{ 287 Stagger: 10 * time.Second, 288 MaxParallel: 1, 289 }, 290 TaskGroups: []*TaskGroup{ 291 { 292 Name: helper.StringToPtr("cache"), 293 Count: helper.IntToPtr(1), 294 RestartPolicy: &RestartPolicy{ 295 Interval: helper.TimeToPtr(5 * time.Minute), 296 Attempts: helper.IntToPtr(10), 297 Delay: helper.TimeToPtr(25 * time.Second), 298 Mode: helper.StringToPtr("delay"), 299 }, 300 EphemeralDisk: &EphemeralDisk{ 301 Sticky: helper.BoolToPtr(false), 302 Migrate: helper.BoolToPtr(false), 303 SizeMB: helper.IntToPtr(300), 304 }, 305 Tasks: []*Task{ 306 { 307 Name: "redis", 308 Driver: "docker", 309 Config: map[string]interface{}{ 310 "image": "redis:3.2", 311 "port_map": map[string]int{ 312 "db": 6379, 313 }, 314 }, 315 Resources: &Resources{ 316 CPU: helper.IntToPtr(500), 317 MemoryMB: helper.IntToPtr(256), 318 IOPS: helper.IntToPtr(0), 319 Networks: []*NetworkResource{ 320 { 321 MBits: helper.IntToPtr(10), 322 DynamicPorts: []Port{ 323 { 324 Label: "db", 325 }, 326 }, 327 }, 328 }, 329 }, 330 Services: []*Service{ 331 { 332 Name: "global-redis-check", 333 Tags: []string{"global", "cache"}, 334 PortLabel: "db", 335 Checks: []ServiceCheck{ 336 { 337 Name: "alive", 338 Type: "tcp", 339 Interval: 10 * time.Second, 340 Timeout: 2 * time.Second, 341 }, 342 }, 343 }, 344 }, 345 KillTimeout: helper.TimeToPtr(5 * time.Second), 346 LogConfig: DefaultLogConfig(), 347 Templates: []*Template{ 348 { 349 SourcePath: helper.StringToPtr(""), 350 DestPath: helper.StringToPtr("local/file.yml"), 351 EmbeddedTmpl: helper.StringToPtr("---"), 352 ChangeMode: helper.StringToPtr("restart"), 353 ChangeSignal: helper.StringToPtr(""), 354 Splay: helper.TimeToPtr(5 * time.Second), 355 Perms: helper.StringToPtr("0644"), 356 LeftDelim: helper.StringToPtr("{{"), 357 RightDelim: helper.StringToPtr("}}"), 358 }, 359 }, 360 }, 361 }, 362 }, 363 }, 364 }, 365 }, 366 367 { 368 name: "periodic", 369 input: &Job{ 370 ID: helper.StringToPtr("bar"), 371 Periodic: &PeriodicConfig{}, 372 }, 373 expected: &Job{ 374 ID: helper.StringToPtr("bar"), 375 ParentID: helper.StringToPtr(""), 376 Name: helper.StringToPtr("bar"), 377 Region: helper.StringToPtr("global"), 378 Type: helper.StringToPtr("service"), 379 Priority: helper.IntToPtr(50), 380 AllAtOnce: helper.BoolToPtr(false), 381 VaultToken: helper.StringToPtr(""), 382 Status: helper.StringToPtr(""), 383 StatusDescription: helper.StringToPtr(""), 384 CreateIndex: helper.Uint64ToPtr(0), 385 ModifyIndex: helper.Uint64ToPtr(0), 386 JobModifyIndex: helper.Uint64ToPtr(0), 387 Periodic: &PeriodicConfig{ 388 Enabled: helper.BoolToPtr(true), 389 Spec: helper.StringToPtr(""), 390 SpecType: helper.StringToPtr(PeriodicSpecCron), 391 ProhibitOverlap: helper.BoolToPtr(false), 392 TimeZone: helper.StringToPtr("UTC"), 393 }, 394 }, 395 }, 396 } 397 398 for _, tc := range testCases { 399 t.Run(tc.name, func(t *testing.T) { 400 tc.input.Canonicalize() 401 if !reflect.DeepEqual(tc.input, tc.expected) { 402 t.Fatalf("Name: %v, expected:\n%#v\nactual:\n%#v", tc.name, tc.expected, tc.input) 403 } 404 }) 405 } 406 } 407 408 func TestJobs_EnforceRegister(t *testing.T) { 409 c, s := makeClient(t, nil, nil) 410 defer s.Stop() 411 jobs := c.Jobs() 412 413 // Listing jobs before registering returns nothing 414 resp, qm, err := jobs.List(nil) 415 if err != nil { 416 t.Fatalf("err: %s", err) 417 } 418 if qm.LastIndex != 0 { 419 t.Fatalf("bad index: %d", qm.LastIndex) 420 } 421 if n := len(resp); n != 0 { 422 t.Fatalf("expected 0 jobs, got: %d", n) 423 } 424 425 // Create a job and attempt to register it with an incorrect index. 426 job := testJob() 427 eval, wm, err := jobs.EnforceRegister(job, 10, nil) 428 if err == nil || !strings.Contains(err.Error(), RegisterEnforceIndexErrPrefix) { 429 t.Fatalf("expected enforcement error: %v", err) 430 } 431 432 // Register 433 eval, wm, err = jobs.EnforceRegister(job, 0, nil) 434 if err != nil { 435 t.Fatalf("err: %s", err) 436 } 437 if eval == "" { 438 t.Fatalf("missing eval id") 439 } 440 assertWriteMeta(t, wm) 441 442 // Query the jobs back out again 443 resp, qm, err = jobs.List(nil) 444 if err != nil { 445 t.Fatalf("err: %s", err) 446 } 447 assertQueryMeta(t, qm) 448 449 // Check that we got the expected response 450 if len(resp) != 1 { 451 t.Fatalf("bad length: %d", len(resp)) 452 } 453 454 if resp[0].ID != *job.ID { 455 t.Fatalf("bad: %#v", resp[0]) 456 } 457 curIndex := resp[0].JobModifyIndex 458 459 // Fail at incorrect index 460 eval, wm, err = jobs.EnforceRegister(job, 123456, nil) 461 if err == nil || !strings.Contains(err.Error(), RegisterEnforceIndexErrPrefix) { 462 t.Fatalf("expected enforcement error: %v", err) 463 } 464 465 // Works at correct index 466 eval, wm, err = jobs.EnforceRegister(job, curIndex, nil) 467 if err != nil { 468 t.Fatalf("err: %s", err) 469 } 470 if eval == "" { 471 t.Fatalf("missing eval id") 472 } 473 assertWriteMeta(t, wm) 474 } 475 476 func TestJobs_Info(t *testing.T) { 477 c, s := makeClient(t, nil, nil) 478 defer s.Stop() 479 jobs := c.Jobs() 480 481 // Trying to retrieve a job by ID before it exists 482 // returns an error 483 _, _, err := jobs.Info("job1", nil) 484 if err == nil || !strings.Contains(err.Error(), "not found") { 485 t.Fatalf("expected not found error, got: %#v", err) 486 } 487 488 // Register the job 489 job := testJob() 490 _, wm, err := jobs.Register(job, nil) 491 if err != nil { 492 t.Fatalf("err: %s", err) 493 } 494 assertWriteMeta(t, wm) 495 496 // Query the job again and ensure it exists 497 result, qm, err := jobs.Info("job1", nil) 498 if err != nil { 499 t.Fatalf("err: %s", err) 500 } 501 assertQueryMeta(t, qm) 502 503 // Check that the result is what we expect 504 if result == nil || *result.ID != *job.ID { 505 t.Fatalf("expect: %#v, got: %#v", job, result) 506 } 507 } 508 509 func TestJobs_PrefixList(t *testing.T) { 510 c, s := makeClient(t, nil, nil) 511 defer s.Stop() 512 jobs := c.Jobs() 513 514 // Listing when nothing exists returns empty 515 results, qm, err := jobs.PrefixList("dummy") 516 if err != nil { 517 t.Fatalf("err: %s", err) 518 } 519 if qm.LastIndex != 0 { 520 t.Fatalf("bad index: %d", qm.LastIndex) 521 } 522 if n := len(results); n != 0 { 523 t.Fatalf("expected 0 jobs, got: %d", n) 524 } 525 526 // Register the job 527 job := testJob() 528 _, wm, err := jobs.Register(job, nil) 529 if err != nil { 530 t.Fatalf("err: %s", err) 531 } 532 assertWriteMeta(t, wm) 533 534 // Query the job again and ensure it exists 535 // Listing when nothing exists returns empty 536 results, qm, err = jobs.PrefixList((*job.ID)[:1]) 537 if err != nil { 538 t.Fatalf("err: %s", err) 539 } 540 541 // Check if we have the right list 542 if len(results) != 1 || results[0].ID != *job.ID { 543 t.Fatalf("bad: %#v", results) 544 } 545 } 546 547 func TestJobs_List(t *testing.T) { 548 c, s := makeClient(t, nil, nil) 549 defer s.Stop() 550 jobs := c.Jobs() 551 552 // Listing when nothing exists returns empty 553 results, qm, err := jobs.List(nil) 554 if err != nil { 555 t.Fatalf("err: %s", err) 556 } 557 if qm.LastIndex != 0 { 558 t.Fatalf("bad index: %d", qm.LastIndex) 559 } 560 if n := len(results); n != 0 { 561 t.Fatalf("expected 0 jobs, got: %d", n) 562 } 563 564 // Register the job 565 job := testJob() 566 _, wm, err := jobs.Register(job, nil) 567 if err != nil { 568 t.Fatalf("err: %s", err) 569 } 570 assertWriteMeta(t, wm) 571 572 // Query the job again and ensure it exists 573 // Listing when nothing exists returns empty 574 results, qm, err = jobs.List(nil) 575 if err != nil { 576 t.Fatalf("err: %s", err) 577 } 578 579 // Check if we have the right list 580 if len(results) != 1 || results[0].ID != *job.ID { 581 t.Fatalf("bad: %#v", results) 582 } 583 } 584 585 func TestJobs_Allocations(t *testing.T) { 586 c, s := makeClient(t, nil, nil) 587 defer s.Stop() 588 jobs := c.Jobs() 589 590 // Looking up by a non-existent job returns nothing 591 allocs, qm, err := jobs.Allocations("job1", true, nil) 592 if err != nil { 593 t.Fatalf("err: %s", err) 594 } 595 if qm.LastIndex != 0 { 596 t.Fatalf("bad index: %d", qm.LastIndex) 597 } 598 if n := len(allocs); n != 0 { 599 t.Fatalf("expected 0 allocs, got: %d", n) 600 } 601 602 // TODO: do something here to create some allocations for 603 // an existing job, lookup again. 604 } 605 606 func TestJobs_Evaluations(t *testing.T) { 607 c, s := makeClient(t, nil, nil) 608 defer s.Stop() 609 jobs := c.Jobs() 610 611 // Looking up by a non-existent job ID returns nothing 612 evals, qm, err := jobs.Evaluations("job1", nil) 613 if err != nil { 614 t.Fatalf("err: %s", err) 615 } 616 if qm.LastIndex != 0 { 617 t.Fatalf("bad index: %d", qm.LastIndex) 618 } 619 if n := len(evals); n != 0 { 620 t.Fatalf("expected 0 evals, got: %d", n) 621 } 622 623 // Insert a job. This also creates an evaluation so we should 624 // be able to query that out after. 625 job := testJob() 626 evalID, wm, err := jobs.Register(job, nil) 627 if err != nil { 628 t.Fatalf("err: %s", err) 629 } 630 assertWriteMeta(t, wm) 631 632 // Look up the evaluations again. 633 evals, qm, err = jobs.Evaluations("job1", nil) 634 if err != nil { 635 t.Fatalf("err: %s", err) 636 } 637 assertQueryMeta(t, qm) 638 639 // Check that we got the evals back, evals are in order most recent to least recent 640 // so the last eval is the original registered eval 641 idx := len(evals) - 1 642 if n := len(evals); n == 0 || evals[idx].ID != evalID { 643 t.Fatalf("expected >= 1 eval (%s), got: %#v", evalID, evals[idx]) 644 } 645 } 646 647 func TestJobs_Deregister(t *testing.T) { 648 c, s := makeClient(t, nil, nil) 649 defer s.Stop() 650 jobs := c.Jobs() 651 652 // Register a new job 653 job := testJob() 654 _, wm, err := jobs.Register(job, nil) 655 if err != nil { 656 t.Fatalf("err: %s", err) 657 } 658 assertWriteMeta(t, wm) 659 660 // Attempting delete on non-existing job returns an error 661 if _, _, err = jobs.Deregister("nope", nil); err != nil { 662 t.Fatalf("unexpected error deregistering job: %v", err) 663 664 } 665 666 // Deleting an existing job works 667 evalID, wm3, err := jobs.Deregister("job1", nil) 668 if err != nil { 669 t.Fatalf("err: %s", err) 670 } 671 assertWriteMeta(t, wm3) 672 if evalID == "" { 673 t.Fatalf("missing eval ID") 674 } 675 676 // Check that the job is really gone 677 result, qm, err := jobs.List(nil) 678 if err != nil { 679 t.Fatalf("err: %s", err) 680 } 681 assertQueryMeta(t, qm) 682 if n := len(result); n != 0 { 683 t.Fatalf("expected 0 jobs, got: %d", n) 684 } 685 } 686 687 func TestJobs_ForceEvaluate(t *testing.T) { 688 c, s := makeClient(t, nil, nil) 689 defer s.Stop() 690 jobs := c.Jobs() 691 692 // Force-eval on a non-existent job fails 693 _, _, err := jobs.ForceEvaluate("job1", nil) 694 if err == nil || !strings.Contains(err.Error(), "not found") { 695 t.Fatalf("expected not found error, got: %#v", err) 696 } 697 698 // Create a new job 699 _, wm, err := jobs.Register(testJob(), nil) 700 if err != nil { 701 t.Fatalf("err: %s", err) 702 } 703 assertWriteMeta(t, wm) 704 705 // Try force-eval again 706 evalID, wm, err := jobs.ForceEvaluate("job1", nil) 707 if err != nil { 708 t.Fatalf("err: %s", err) 709 } 710 assertWriteMeta(t, wm) 711 712 // Retrieve the evals and see if we get a matching one 713 evals, qm, err := jobs.Evaluations("job1", nil) 714 if err != nil { 715 t.Fatalf("err: %s", err) 716 } 717 assertQueryMeta(t, qm) 718 for _, eval := range evals { 719 if eval.ID == evalID { 720 return 721 } 722 } 723 t.Fatalf("evaluation %q missing", evalID) 724 } 725 726 func TestJobs_PeriodicForce(t *testing.T) { 727 c, s := makeClient(t, nil, nil) 728 defer s.Stop() 729 jobs := c.Jobs() 730 731 // Force-eval on a non-existent job fails 732 _, _, err := jobs.PeriodicForce("job1", nil) 733 if err == nil || !strings.Contains(err.Error(), "not found") { 734 t.Fatalf("expected not found error, got: %#v", err) 735 } 736 737 // Create a new job 738 job := testPeriodicJob() 739 _, _, err = jobs.Register(job, nil) 740 if err != nil { 741 t.Fatalf("err: %s", err) 742 } 743 744 testutil.WaitForResult(func() (bool, error) { 745 out, _, err := jobs.Info(*job.ID, nil) 746 if err != nil || out == nil || *out.ID != *job.ID { 747 return false, err 748 } 749 return true, nil 750 }, func(err error) { 751 t.Fatalf("err: %s", err) 752 }) 753 754 // Try force again 755 evalID, wm, err := jobs.PeriodicForce(*job.ID, nil) 756 if err != nil { 757 t.Fatalf("err: %s", err) 758 } 759 assertWriteMeta(t, wm) 760 761 if evalID == "" { 762 t.Fatalf("empty evalID") 763 } 764 765 // Retrieve the eval 766 evals := c.Evaluations() 767 eval, qm, err := evals.Info(evalID, nil) 768 if err != nil { 769 t.Fatalf("err: %s", err) 770 } 771 assertQueryMeta(t, qm) 772 if eval.ID == evalID { 773 return 774 } 775 t.Fatalf("evaluation %q missing", evalID) 776 } 777 778 func TestJobs_Plan(t *testing.T) { 779 c, s := makeClient(t, nil, nil) 780 defer s.Stop() 781 jobs := c.Jobs() 782 783 // Create a job and attempt to register it 784 job := testJob() 785 eval, wm, err := jobs.Register(job, nil) 786 if err != nil { 787 t.Fatalf("err: %s", err) 788 } 789 if eval == "" { 790 t.Fatalf("missing eval id") 791 } 792 assertWriteMeta(t, wm) 793 794 // Check that passing a nil job fails 795 if _, _, err := jobs.Plan(nil, true, nil); err == nil { 796 t.Fatalf("expect an error when job isn't provided") 797 } 798 799 // Make a plan request 800 planResp, wm, err := jobs.Plan(job, true, nil) 801 if err != nil { 802 t.Fatalf("err: %s", err) 803 } 804 if planResp == nil { 805 t.Fatalf("nil response") 806 } 807 808 if planResp.JobModifyIndex == 0 { 809 t.Fatalf("bad JobModifyIndex value: %#v", planResp) 810 } 811 if planResp.Diff == nil { 812 t.Fatalf("got nil diff: %#v", planResp) 813 } 814 if planResp.Annotations == nil { 815 t.Fatalf("got nil annotations: %#v", planResp) 816 } 817 // Can make this assertion because there are no clients. 818 if len(planResp.CreatedEvals) == 0 { 819 t.Fatalf("got no CreatedEvals: %#v", planResp) 820 } 821 822 // Make a plan request w/o the diff 823 planResp, wm, err = jobs.Plan(job, false, nil) 824 if err != nil { 825 t.Fatalf("err: %s", err) 826 } 827 assertWriteMeta(t, wm) 828 829 if planResp == nil { 830 t.Fatalf("nil response") 831 } 832 833 if planResp.JobModifyIndex == 0 { 834 t.Fatalf("bad JobModifyIndex value: %d", planResp.JobModifyIndex) 835 } 836 if planResp.Diff != nil { 837 t.Fatalf("got non-nil diff: %#v", planResp) 838 } 839 if planResp.Annotations == nil { 840 t.Fatalf("got nil annotations: %#v", planResp) 841 } 842 // Can make this assertion because there are no clients. 843 if len(planResp.CreatedEvals) == 0 { 844 t.Fatalf("got no CreatedEvals: %#v", planResp) 845 } 846 } 847 848 func TestJobs_JobSummary(t *testing.T) { 849 c, s := makeClient(t, nil, nil) 850 defer s.Stop() 851 jobs := c.Jobs() 852 853 // Trying to retrieve a job summary before the job exists 854 // returns an error 855 _, _, err := jobs.Summary("job1", nil) 856 if err == nil || !strings.Contains(err.Error(), "not found") { 857 t.Fatalf("expected not found error, got: %#v", err) 858 } 859 860 // Register the job 861 job := testJob() 862 taskName := job.TaskGroups[0].Name 863 _, wm, err := jobs.Register(job, nil) 864 if err != nil { 865 t.Fatalf("err: %s", err) 866 } 867 assertWriteMeta(t, wm) 868 869 // Query the job summary again and ensure it exists 870 result, qm, err := jobs.Summary("job1", nil) 871 if err != nil { 872 t.Fatalf("err: %s", err) 873 } 874 assertQueryMeta(t, qm) 875 876 // Check that the result is what we expect 877 if *job.ID != result.JobID { 878 t.Fatalf("err: expected job id of %s saw %s", *job.ID, result.JobID) 879 } 880 if _, ok := result.Summary[*taskName]; !ok { 881 t.Fatalf("err: unable to find %s key in job summary", *taskName) 882 } 883 } 884 885 func TestJobs_NewBatchJob(t *testing.T) { 886 job := NewBatchJob("job1", "myjob", "region1", 5) 887 expect := &Job{ 888 Region: helper.StringToPtr("region1"), 889 ID: helper.StringToPtr("job1"), 890 Name: helper.StringToPtr("myjob"), 891 Type: helper.StringToPtr(JobTypeBatch), 892 Priority: helper.IntToPtr(5), 893 } 894 if !reflect.DeepEqual(job, expect) { 895 t.Fatalf("expect: %#v, got: %#v", expect, job) 896 } 897 } 898 899 func TestJobs_NewServiceJob(t *testing.T) { 900 job := NewServiceJob("job1", "myjob", "region1", 5) 901 expect := &Job{ 902 Region: helper.StringToPtr("region1"), 903 ID: helper.StringToPtr("job1"), 904 Name: helper.StringToPtr("myjob"), 905 Type: helper.StringToPtr(JobTypeService), 906 Priority: helper.IntToPtr(5), 907 } 908 if !reflect.DeepEqual(job, expect) { 909 t.Fatalf("expect: %#v, got: %#v", expect, job) 910 } 911 } 912 913 func TestJobs_SetMeta(t *testing.T) { 914 job := &Job{Meta: nil} 915 916 // Initializes a nil map 917 out := job.SetMeta("foo", "bar") 918 if job.Meta == nil { 919 t.Fatalf("should initialize metadata") 920 } 921 922 // Check that the job was returned 923 if job != out { 924 t.Fatalf("expect: %#v, got: %#v", job, out) 925 } 926 927 // Setting another pair is additive 928 job.SetMeta("baz", "zip") 929 expect := map[string]string{"foo": "bar", "baz": "zip"} 930 if !reflect.DeepEqual(job.Meta, expect) { 931 t.Fatalf("expect: %#v, got: %#v", expect, job.Meta) 932 } 933 } 934 935 func TestJobs_Constrain(t *testing.T) { 936 job := &Job{Constraints: nil} 937 938 // Create and add a constraint 939 out := job.Constrain(NewConstraint("kernel.name", "=", "darwin")) 940 if n := len(job.Constraints); n != 1 { 941 t.Fatalf("expected 1 constraint, got: %d", n) 942 } 943 944 // Check that the job was returned 945 if job != out { 946 t.Fatalf("expect: %#v, got: %#v", job, out) 947 } 948 949 // Adding another constraint preserves the original 950 job.Constrain(NewConstraint("memory.totalbytes", ">=", "128000000")) 951 expect := []*Constraint{ 952 &Constraint{ 953 LTarget: "kernel.name", 954 RTarget: "darwin", 955 Operand: "=", 956 }, 957 &Constraint{ 958 LTarget: "memory.totalbytes", 959 RTarget: "128000000", 960 Operand: ">=", 961 }, 962 } 963 if !reflect.DeepEqual(job.Constraints, expect) { 964 t.Fatalf("expect: %#v, got: %#v", expect, job.Constraints) 965 } 966 } 967 968 func TestJobs_Sort(t *testing.T) { 969 jobs := []*JobListStub{ 970 &JobListStub{ID: "job2"}, 971 &JobListStub{ID: "job0"}, 972 &JobListStub{ID: "job1"}, 973 } 974 sort.Sort(JobIDSort(jobs)) 975 976 expect := []*JobListStub{ 977 &JobListStub{ID: "job0"}, 978 &JobListStub{ID: "job1"}, 979 &JobListStub{ID: "job2"}, 980 } 981 if !reflect.DeepEqual(jobs, expect) { 982 t.Fatalf("\n\n%#v\n\n%#v", jobs, expect) 983 } 984 }