github.com/nir0s/nomad@v0.8.7-rc1/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/hashicorp/nomad/helper" 11 "github.com/hashicorp/nomad/nomad/mock" 12 "github.com/hashicorp/nomad/testutil" 13 "github.com/kr/pretty" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestJobs_Register(t *testing.T) { 19 t.Parallel() 20 require := require.New(t) 21 22 c, s := makeClient(t, nil, nil) 23 defer s.Stop() 24 jobs := c.Jobs() 25 26 // Listing jobs before registering returns nothing 27 resp, _, err := jobs.List(nil) 28 require.Nil(err) 29 require.Emptyf(resp, "expected 0 jobs, got: %d", len(resp)) 30 31 // Create a job and attempt to register it 32 job := testJob() 33 resp2, wm, err := jobs.Register(job, nil) 34 require.Nil(err) 35 require.NotNil(resp2) 36 require.NotEmpty(resp2.EvalID) 37 assertWriteMeta(t, wm) 38 39 // Query the jobs back out again 40 resp, qm, err := jobs.List(nil) 41 assertQueryMeta(t, qm) 42 require.Nil(err) 43 44 // Check that we got the expected response 45 if len(resp) != 1 || resp[0].ID != *job.ID { 46 t.Fatalf("bad: %#v", resp[0]) 47 } 48 } 49 50 func TestJobs_Parse(t *testing.T) { 51 t.Parallel() 52 c, s := makeClient(t, nil, nil) 53 defer s.Stop() 54 55 jobs := c.Jobs() 56 57 checkJob := func(job *Job, expectedRegion string) { 58 if job == nil { 59 t.Fatal("job should not be nil") 60 } 61 62 region := job.Region 63 64 if region == nil { 65 if expectedRegion != "" { 66 t.Fatalf("expected job region to be '%s' but was unset", expectedRegion) 67 } 68 } else { 69 if expectedRegion != *region { 70 t.Fatalf("expected job region '%s', but got '%s'", expectedRegion, *region) 71 } 72 } 73 } 74 job, err := jobs.ParseHCL(mock.HCL(), true) 75 if err != nil { 76 t.Fatalf("err: %s", err) 77 } 78 checkJob(job, "global") 79 80 job, err = jobs.ParseHCL(mock.HCL(), false) 81 if err != nil { 82 t.Fatalf("err: %s", err) 83 } 84 checkJob(job, "") 85 } 86 87 func TestJobs_Validate(t *testing.T) { 88 t.Parallel() 89 c, s := makeClient(t, nil, nil) 90 defer s.Stop() 91 jobs := c.Jobs() 92 93 // Create a job and attempt to register it 94 job := testJob() 95 resp, _, err := jobs.Validate(job, nil) 96 if err != nil { 97 t.Fatalf("err: %s", err) 98 } 99 100 if len(resp.ValidationErrors) != 0 { 101 t.Fatalf("bad %v", resp) 102 } 103 104 job.ID = nil 105 resp1, _, err := jobs.Validate(job, nil) 106 if err != nil { 107 t.Fatalf("err: %v", err) 108 } 109 110 if len(resp1.ValidationErrors) == 0 { 111 t.Fatalf("bad %v", resp1) 112 } 113 } 114 115 func TestJobs_Canonicalize(t *testing.T) { 116 t.Parallel() 117 testCases := []struct { 118 name string 119 expected *Job 120 input *Job 121 }{ 122 { 123 name: "empty", 124 input: &Job{ 125 TaskGroups: []*TaskGroup{ 126 { 127 Tasks: []*Task{ 128 {}, 129 }, 130 }, 131 }, 132 }, 133 expected: &Job{ 134 ID: helper.StringToPtr(""), 135 Name: helper.StringToPtr(""), 136 Region: helper.StringToPtr("global"), 137 Namespace: helper.StringToPtr(DefaultNamespace), 138 Type: helper.StringToPtr("service"), 139 ParentID: helper.StringToPtr(""), 140 Priority: helper.IntToPtr(50), 141 AllAtOnce: helper.BoolToPtr(false), 142 VaultToken: helper.StringToPtr(""), 143 Status: helper.StringToPtr(""), 144 StatusDescription: helper.StringToPtr(""), 145 Stop: helper.BoolToPtr(false), 146 Stable: helper.BoolToPtr(false), 147 Version: helper.Uint64ToPtr(0), 148 CreateIndex: helper.Uint64ToPtr(0), 149 ModifyIndex: helper.Uint64ToPtr(0), 150 JobModifyIndex: helper.Uint64ToPtr(0), 151 TaskGroups: []*TaskGroup{ 152 { 153 Name: helper.StringToPtr(""), 154 Count: helper.IntToPtr(1), 155 EphemeralDisk: &EphemeralDisk{ 156 Sticky: helper.BoolToPtr(false), 157 Migrate: helper.BoolToPtr(false), 158 SizeMB: helper.IntToPtr(300), 159 }, 160 RestartPolicy: &RestartPolicy{ 161 Delay: helper.TimeToPtr(15 * time.Second), 162 Attempts: helper.IntToPtr(2), 163 Interval: helper.TimeToPtr(30 * time.Minute), 164 Mode: helper.StringToPtr("fail"), 165 }, 166 ReschedulePolicy: &ReschedulePolicy{ 167 Attempts: helper.IntToPtr(0), 168 Interval: helper.TimeToPtr(0), 169 DelayFunction: helper.StringToPtr("exponential"), 170 Delay: helper.TimeToPtr(30 * time.Second), 171 MaxDelay: helper.TimeToPtr(1 * time.Hour), 172 Unlimited: helper.BoolToPtr(true), 173 }, 174 Migrate: DefaultMigrateStrategy(), 175 Tasks: []*Task{ 176 { 177 KillTimeout: helper.TimeToPtr(5 * time.Second), 178 LogConfig: DefaultLogConfig(), 179 Resources: DefaultResources(), 180 }, 181 }, 182 }, 183 }, 184 }, 185 }, 186 { 187 name: "partial", 188 input: &Job{ 189 Name: helper.StringToPtr("foo"), 190 Namespace: helper.StringToPtr("bar"), 191 ID: helper.StringToPtr("bar"), 192 ParentID: helper.StringToPtr("lol"), 193 TaskGroups: []*TaskGroup{ 194 { 195 Name: helper.StringToPtr("bar"), 196 Tasks: []*Task{ 197 { 198 Name: "task1", 199 }, 200 }, 201 }, 202 }, 203 }, 204 expected: &Job{ 205 Namespace: helper.StringToPtr("bar"), 206 ID: helper.StringToPtr("bar"), 207 Name: helper.StringToPtr("foo"), 208 Region: helper.StringToPtr("global"), 209 Type: helper.StringToPtr("service"), 210 ParentID: helper.StringToPtr("lol"), 211 Priority: helper.IntToPtr(50), 212 AllAtOnce: helper.BoolToPtr(false), 213 VaultToken: helper.StringToPtr(""), 214 Stop: helper.BoolToPtr(false), 215 Stable: helper.BoolToPtr(false), 216 Version: helper.Uint64ToPtr(0), 217 Status: helper.StringToPtr(""), 218 StatusDescription: helper.StringToPtr(""), 219 CreateIndex: helper.Uint64ToPtr(0), 220 ModifyIndex: helper.Uint64ToPtr(0), 221 JobModifyIndex: helper.Uint64ToPtr(0), 222 TaskGroups: []*TaskGroup{ 223 { 224 Name: helper.StringToPtr("bar"), 225 Count: helper.IntToPtr(1), 226 EphemeralDisk: &EphemeralDisk{ 227 Sticky: helper.BoolToPtr(false), 228 Migrate: helper.BoolToPtr(false), 229 SizeMB: helper.IntToPtr(300), 230 }, 231 RestartPolicy: &RestartPolicy{ 232 Delay: helper.TimeToPtr(15 * time.Second), 233 Attempts: helper.IntToPtr(2), 234 Interval: helper.TimeToPtr(30 * time.Minute), 235 Mode: helper.StringToPtr("fail"), 236 }, 237 ReschedulePolicy: &ReschedulePolicy{ 238 Attempts: helper.IntToPtr(0), 239 Interval: helper.TimeToPtr(0), 240 DelayFunction: helper.StringToPtr("exponential"), 241 Delay: helper.TimeToPtr(30 * time.Second), 242 MaxDelay: helper.TimeToPtr(1 * time.Hour), 243 Unlimited: helper.BoolToPtr(true), 244 }, 245 Migrate: DefaultMigrateStrategy(), 246 Tasks: []*Task{ 247 { 248 Name: "task1", 249 LogConfig: DefaultLogConfig(), 250 Resources: DefaultResources(), 251 KillTimeout: helper.TimeToPtr(5 * time.Second), 252 }, 253 }, 254 }, 255 }, 256 }, 257 }, 258 { 259 name: "example_template", 260 input: &Job{ 261 ID: helper.StringToPtr("example_template"), 262 Name: helper.StringToPtr("example_template"), 263 Datacenters: []string{"dc1"}, 264 Type: helper.StringToPtr("service"), 265 Update: &UpdateStrategy{ 266 MaxParallel: helper.IntToPtr(1), 267 }, 268 TaskGroups: []*TaskGroup{ 269 { 270 Name: helper.StringToPtr("cache"), 271 Count: helper.IntToPtr(1), 272 RestartPolicy: &RestartPolicy{ 273 Interval: helper.TimeToPtr(5 * time.Minute), 274 Attempts: helper.IntToPtr(10), 275 Delay: helper.TimeToPtr(25 * time.Second), 276 Mode: helper.StringToPtr("delay"), 277 }, 278 EphemeralDisk: &EphemeralDisk{ 279 SizeMB: helper.IntToPtr(300), 280 }, 281 Tasks: []*Task{ 282 { 283 Name: "redis", 284 Driver: "docker", 285 Config: map[string]interface{}{ 286 "image": "redis:3.2", 287 "port_map": []map[string]int{{ 288 "db": 6379, 289 }}, 290 }, 291 Resources: &Resources{ 292 CPU: helper.IntToPtr(500), 293 MemoryMB: helper.IntToPtr(256), 294 Networks: []*NetworkResource{ 295 { 296 MBits: helper.IntToPtr(10), 297 DynamicPorts: []Port{ 298 { 299 Label: "db", 300 }, 301 }, 302 }, 303 }, 304 }, 305 Services: []*Service{ 306 { 307 Name: "redis-cache", 308 Tags: []string{"global", "cache"}, 309 CanaryTags: []string{"canary", "global", "cache"}, 310 PortLabel: "db", 311 Checks: []ServiceCheck{ 312 { 313 Name: "alive", 314 Type: "tcp", 315 Interval: 10 * time.Second, 316 Timeout: 2 * time.Second, 317 }, 318 }, 319 }, 320 }, 321 Templates: []*Template{ 322 { 323 EmbeddedTmpl: helper.StringToPtr("---"), 324 DestPath: helper.StringToPtr("local/file.yml"), 325 }, 326 { 327 EmbeddedTmpl: helper.StringToPtr("FOO=bar\n"), 328 DestPath: helper.StringToPtr("local/file.env"), 329 Envvars: helper.BoolToPtr(true), 330 VaultGrace: helper.TimeToPtr(3 * time.Second), 331 }, 332 }, 333 }, 334 }, 335 }, 336 }, 337 }, 338 expected: &Job{ 339 Namespace: helper.StringToPtr(DefaultNamespace), 340 ID: helper.StringToPtr("example_template"), 341 Name: helper.StringToPtr("example_template"), 342 ParentID: helper.StringToPtr(""), 343 Priority: helper.IntToPtr(50), 344 Region: helper.StringToPtr("global"), 345 Type: helper.StringToPtr("service"), 346 AllAtOnce: helper.BoolToPtr(false), 347 VaultToken: helper.StringToPtr(""), 348 Stop: helper.BoolToPtr(false), 349 Stable: helper.BoolToPtr(false), 350 Version: helper.Uint64ToPtr(0), 351 Status: helper.StringToPtr(""), 352 StatusDescription: helper.StringToPtr(""), 353 CreateIndex: helper.Uint64ToPtr(0), 354 ModifyIndex: helper.Uint64ToPtr(0), 355 JobModifyIndex: helper.Uint64ToPtr(0), 356 Datacenters: []string{"dc1"}, 357 Update: &UpdateStrategy{ 358 Stagger: helper.TimeToPtr(30 * time.Second), 359 MaxParallel: helper.IntToPtr(1), 360 HealthCheck: helper.StringToPtr("checks"), 361 MinHealthyTime: helper.TimeToPtr(10 * time.Second), 362 HealthyDeadline: helper.TimeToPtr(5 * time.Minute), 363 ProgressDeadline: helper.TimeToPtr(10 * time.Minute), 364 AutoRevert: helper.BoolToPtr(false), 365 Canary: helper.IntToPtr(0), 366 }, 367 TaskGroups: []*TaskGroup{ 368 { 369 Name: helper.StringToPtr("cache"), 370 Count: helper.IntToPtr(1), 371 RestartPolicy: &RestartPolicy{ 372 Interval: helper.TimeToPtr(5 * time.Minute), 373 Attempts: helper.IntToPtr(10), 374 Delay: helper.TimeToPtr(25 * time.Second), 375 Mode: helper.StringToPtr("delay"), 376 }, 377 ReschedulePolicy: &ReschedulePolicy{ 378 Attempts: helper.IntToPtr(0), 379 Interval: helper.TimeToPtr(0), 380 DelayFunction: helper.StringToPtr("exponential"), 381 Delay: helper.TimeToPtr(30 * time.Second), 382 MaxDelay: helper.TimeToPtr(1 * time.Hour), 383 Unlimited: helper.BoolToPtr(true), 384 }, 385 EphemeralDisk: &EphemeralDisk{ 386 Sticky: helper.BoolToPtr(false), 387 Migrate: helper.BoolToPtr(false), 388 SizeMB: helper.IntToPtr(300), 389 }, 390 391 Update: &UpdateStrategy{ 392 Stagger: helper.TimeToPtr(30 * time.Second), 393 MaxParallel: helper.IntToPtr(1), 394 HealthCheck: helper.StringToPtr("checks"), 395 MinHealthyTime: helper.TimeToPtr(10 * time.Second), 396 HealthyDeadline: helper.TimeToPtr(5 * time.Minute), 397 ProgressDeadline: helper.TimeToPtr(10 * time.Minute), 398 AutoRevert: helper.BoolToPtr(false), 399 Canary: helper.IntToPtr(0), 400 }, 401 Migrate: DefaultMigrateStrategy(), 402 Tasks: []*Task{ 403 { 404 Name: "redis", 405 Driver: "docker", 406 Config: map[string]interface{}{ 407 "image": "redis:3.2", 408 "port_map": []map[string]int{{ 409 "db": 6379, 410 }}, 411 }, 412 Resources: &Resources{ 413 CPU: helper.IntToPtr(500), 414 MemoryMB: helper.IntToPtr(256), 415 IOPS: helper.IntToPtr(0), 416 Networks: []*NetworkResource{ 417 { 418 MBits: helper.IntToPtr(10), 419 DynamicPorts: []Port{ 420 { 421 Label: "db", 422 }, 423 }, 424 }, 425 }, 426 }, 427 Services: []*Service{ 428 { 429 Name: "redis-cache", 430 Tags: []string{"global", "cache"}, 431 CanaryTags: []string{"canary", "global", "cache"}, 432 PortLabel: "db", 433 AddressMode: "auto", 434 Checks: []ServiceCheck{ 435 { 436 Name: "alive", 437 Type: "tcp", 438 Interval: 10 * time.Second, 439 Timeout: 2 * time.Second, 440 }, 441 }, 442 }, 443 }, 444 KillTimeout: helper.TimeToPtr(5 * time.Second), 445 LogConfig: DefaultLogConfig(), 446 Templates: []*Template{ 447 { 448 SourcePath: helper.StringToPtr(""), 449 DestPath: helper.StringToPtr("local/file.yml"), 450 EmbeddedTmpl: helper.StringToPtr("---"), 451 ChangeMode: helper.StringToPtr("restart"), 452 ChangeSignal: helper.StringToPtr(""), 453 Splay: helper.TimeToPtr(5 * time.Second), 454 Perms: helper.StringToPtr("0644"), 455 LeftDelim: helper.StringToPtr("{{"), 456 RightDelim: helper.StringToPtr("}}"), 457 Envvars: helper.BoolToPtr(false), 458 VaultGrace: helper.TimeToPtr(15 * time.Second), 459 }, 460 { 461 SourcePath: helper.StringToPtr(""), 462 DestPath: helper.StringToPtr("local/file.env"), 463 EmbeddedTmpl: helper.StringToPtr("FOO=bar\n"), 464 ChangeMode: helper.StringToPtr("restart"), 465 ChangeSignal: helper.StringToPtr(""), 466 Splay: helper.TimeToPtr(5 * time.Second), 467 Perms: helper.StringToPtr("0644"), 468 LeftDelim: helper.StringToPtr("{{"), 469 RightDelim: helper.StringToPtr("}}"), 470 Envvars: helper.BoolToPtr(true), 471 VaultGrace: helper.TimeToPtr(3 * time.Second), 472 }, 473 }, 474 }, 475 }, 476 }, 477 }, 478 }, 479 }, 480 481 { 482 name: "periodic", 483 input: &Job{ 484 ID: helper.StringToPtr("bar"), 485 Periodic: &PeriodicConfig{}, 486 }, 487 expected: &Job{ 488 Namespace: helper.StringToPtr(DefaultNamespace), 489 ID: helper.StringToPtr("bar"), 490 ParentID: helper.StringToPtr(""), 491 Name: helper.StringToPtr("bar"), 492 Region: helper.StringToPtr("global"), 493 Type: helper.StringToPtr("service"), 494 Priority: helper.IntToPtr(50), 495 AllAtOnce: helper.BoolToPtr(false), 496 VaultToken: helper.StringToPtr(""), 497 Stop: helper.BoolToPtr(false), 498 Stable: helper.BoolToPtr(false), 499 Version: helper.Uint64ToPtr(0), 500 Status: helper.StringToPtr(""), 501 StatusDescription: helper.StringToPtr(""), 502 CreateIndex: helper.Uint64ToPtr(0), 503 ModifyIndex: helper.Uint64ToPtr(0), 504 JobModifyIndex: helper.Uint64ToPtr(0), 505 Periodic: &PeriodicConfig{ 506 Enabled: helper.BoolToPtr(true), 507 Spec: helper.StringToPtr(""), 508 SpecType: helper.StringToPtr(PeriodicSpecCron), 509 ProhibitOverlap: helper.BoolToPtr(false), 510 TimeZone: helper.StringToPtr("UTC"), 511 }, 512 }, 513 }, 514 515 { 516 name: "update_merge", 517 input: &Job{ 518 Name: helper.StringToPtr("foo"), 519 ID: helper.StringToPtr("bar"), 520 ParentID: helper.StringToPtr("lol"), 521 Update: &UpdateStrategy{ 522 Stagger: helper.TimeToPtr(1 * time.Second), 523 MaxParallel: helper.IntToPtr(1), 524 HealthCheck: helper.StringToPtr("checks"), 525 MinHealthyTime: helper.TimeToPtr(10 * time.Second), 526 HealthyDeadline: helper.TimeToPtr(6 * time.Minute), 527 ProgressDeadline: helper.TimeToPtr(7 * time.Minute), 528 AutoRevert: helper.BoolToPtr(false), 529 Canary: helper.IntToPtr(0), 530 }, 531 TaskGroups: []*TaskGroup{ 532 { 533 Name: helper.StringToPtr("bar"), 534 Update: &UpdateStrategy{ 535 Stagger: helper.TimeToPtr(2 * time.Second), 536 MaxParallel: helper.IntToPtr(2), 537 HealthCheck: helper.StringToPtr("manual"), 538 MinHealthyTime: helper.TimeToPtr(1 * time.Second), 539 AutoRevert: helper.BoolToPtr(true), 540 Canary: helper.IntToPtr(1), 541 }, 542 Tasks: []*Task{ 543 { 544 Name: "task1", 545 }, 546 }, 547 }, 548 { 549 Name: helper.StringToPtr("baz"), 550 Tasks: []*Task{ 551 { 552 Name: "task1", 553 }, 554 }, 555 }, 556 }, 557 }, 558 expected: &Job{ 559 Namespace: helper.StringToPtr(DefaultNamespace), 560 ID: helper.StringToPtr("bar"), 561 Name: helper.StringToPtr("foo"), 562 Region: helper.StringToPtr("global"), 563 Type: helper.StringToPtr("service"), 564 ParentID: helper.StringToPtr("lol"), 565 Priority: helper.IntToPtr(50), 566 AllAtOnce: helper.BoolToPtr(false), 567 VaultToken: helper.StringToPtr(""), 568 Stop: helper.BoolToPtr(false), 569 Stable: helper.BoolToPtr(false), 570 Version: helper.Uint64ToPtr(0), 571 Status: helper.StringToPtr(""), 572 StatusDescription: helper.StringToPtr(""), 573 CreateIndex: helper.Uint64ToPtr(0), 574 ModifyIndex: helper.Uint64ToPtr(0), 575 JobModifyIndex: helper.Uint64ToPtr(0), 576 Update: &UpdateStrategy{ 577 Stagger: helper.TimeToPtr(1 * time.Second), 578 MaxParallel: helper.IntToPtr(1), 579 HealthCheck: helper.StringToPtr("checks"), 580 MinHealthyTime: helper.TimeToPtr(10 * time.Second), 581 HealthyDeadline: helper.TimeToPtr(6 * time.Minute), 582 ProgressDeadline: helper.TimeToPtr(7 * time.Minute), 583 AutoRevert: helper.BoolToPtr(false), 584 Canary: helper.IntToPtr(0), 585 }, 586 TaskGroups: []*TaskGroup{ 587 { 588 Name: helper.StringToPtr("bar"), 589 Count: helper.IntToPtr(1), 590 EphemeralDisk: &EphemeralDisk{ 591 Sticky: helper.BoolToPtr(false), 592 Migrate: helper.BoolToPtr(false), 593 SizeMB: helper.IntToPtr(300), 594 }, 595 RestartPolicy: &RestartPolicy{ 596 Delay: helper.TimeToPtr(15 * time.Second), 597 Attempts: helper.IntToPtr(2), 598 Interval: helper.TimeToPtr(30 * time.Minute), 599 Mode: helper.StringToPtr("fail"), 600 }, 601 ReschedulePolicy: &ReschedulePolicy{ 602 Attempts: helper.IntToPtr(0), 603 Interval: helper.TimeToPtr(0), 604 DelayFunction: helper.StringToPtr("exponential"), 605 Delay: helper.TimeToPtr(30 * time.Second), 606 MaxDelay: helper.TimeToPtr(1 * time.Hour), 607 Unlimited: helper.BoolToPtr(true), 608 }, 609 Update: &UpdateStrategy{ 610 Stagger: helper.TimeToPtr(2 * time.Second), 611 MaxParallel: helper.IntToPtr(2), 612 HealthCheck: helper.StringToPtr("manual"), 613 MinHealthyTime: helper.TimeToPtr(1 * time.Second), 614 HealthyDeadline: helper.TimeToPtr(6 * time.Minute), 615 ProgressDeadline: helper.TimeToPtr(7 * time.Minute), 616 AutoRevert: helper.BoolToPtr(true), 617 Canary: helper.IntToPtr(1), 618 }, 619 Migrate: DefaultMigrateStrategy(), 620 Tasks: []*Task{ 621 { 622 Name: "task1", 623 LogConfig: DefaultLogConfig(), 624 Resources: DefaultResources(), 625 KillTimeout: helper.TimeToPtr(5 * time.Second), 626 }, 627 }, 628 }, 629 { 630 Name: helper.StringToPtr("baz"), 631 Count: helper.IntToPtr(1), 632 EphemeralDisk: &EphemeralDisk{ 633 Sticky: helper.BoolToPtr(false), 634 Migrate: helper.BoolToPtr(false), 635 SizeMB: helper.IntToPtr(300), 636 }, 637 RestartPolicy: &RestartPolicy{ 638 Delay: helper.TimeToPtr(15 * time.Second), 639 Attempts: helper.IntToPtr(2), 640 Interval: helper.TimeToPtr(30 * time.Minute), 641 Mode: helper.StringToPtr("fail"), 642 }, 643 ReschedulePolicy: &ReschedulePolicy{ 644 Attempts: helper.IntToPtr(0), 645 Interval: helper.TimeToPtr(0), 646 DelayFunction: helper.StringToPtr("exponential"), 647 Delay: helper.TimeToPtr(30 * time.Second), 648 MaxDelay: helper.TimeToPtr(1 * time.Hour), 649 Unlimited: helper.BoolToPtr(true), 650 }, 651 Update: &UpdateStrategy{ 652 Stagger: helper.TimeToPtr(1 * time.Second), 653 MaxParallel: helper.IntToPtr(1), 654 HealthCheck: helper.StringToPtr("checks"), 655 MinHealthyTime: helper.TimeToPtr(10 * time.Second), 656 HealthyDeadline: helper.TimeToPtr(6 * time.Minute), 657 ProgressDeadline: helper.TimeToPtr(7 * time.Minute), 658 AutoRevert: helper.BoolToPtr(false), 659 Canary: helper.IntToPtr(0), 660 }, 661 Migrate: DefaultMigrateStrategy(), 662 Tasks: []*Task{ 663 { 664 Name: "task1", 665 LogConfig: DefaultLogConfig(), 666 Resources: DefaultResources(), 667 KillTimeout: helper.TimeToPtr(5 * time.Second), 668 }, 669 }, 670 }, 671 }, 672 }, 673 }, 674 } 675 676 for _, tc := range testCases { 677 t.Run(tc.name, func(t *testing.T) { 678 tc.input.Canonicalize() 679 if !reflect.DeepEqual(tc.input, tc.expected) { 680 t.Fatalf("Name: %v, Diffs:\n%v", tc.name, pretty.Diff(tc.expected, tc.input)) 681 } 682 }) 683 } 684 } 685 686 func TestJobs_EnforceRegister(t *testing.T) { 687 t.Parallel() 688 require := require.New(t) 689 c, s := makeClient(t, nil, nil) 690 defer s.Stop() 691 jobs := c.Jobs() 692 693 // Listing jobs before registering returns nothing 694 resp, _, err := jobs.List(nil) 695 require.Nil(err) 696 require.Empty(resp) 697 698 // Create a job and attempt to register it with an incorrect index. 699 job := testJob() 700 resp2, _, err := jobs.EnforceRegister(job, 10, nil) 701 require.NotNil(err) 702 require.Contains(err.Error(), RegisterEnforceIndexErrPrefix) 703 704 // Register 705 resp2, wm, err := jobs.EnforceRegister(job, 0, nil) 706 require.Nil(err) 707 require.NotNil(resp2) 708 require.NotZero(resp2.EvalID) 709 assertWriteMeta(t, wm) 710 711 // Query the jobs back out again 712 resp, qm, err := jobs.List(nil) 713 require.Nil(err) 714 require.Len(resp, 1) 715 require.Equal(*job.ID, resp[0].ID) 716 assertQueryMeta(t, qm) 717 718 // Fail at incorrect index 719 curIndex := resp[0].JobModifyIndex 720 resp2, _, err = jobs.EnforceRegister(job, 123456, nil) 721 require.NotNil(err) 722 require.Contains(err.Error(), RegisterEnforceIndexErrPrefix) 723 724 // Works at correct index 725 resp3, wm, err := jobs.EnforceRegister(job, curIndex, nil) 726 require.Nil(err) 727 require.NotNil(resp3) 728 require.NotZero(resp3.EvalID) 729 assertWriteMeta(t, wm) 730 } 731 732 func TestJobs_Revert(t *testing.T) { 733 t.Parallel() 734 c, s := makeClient(t, nil, nil) 735 defer s.Stop() 736 jobs := c.Jobs() 737 738 // Register twice 739 job := testJob() 740 resp, wm, err := jobs.Register(job, nil) 741 if err != nil { 742 t.Fatalf("err: %s", err) 743 } 744 if resp == nil || resp.EvalID == "" { 745 t.Fatalf("missing eval id") 746 } 747 assertWriteMeta(t, wm) 748 749 job.Meta = map[string]string{"foo": "new"} 750 resp, wm, err = jobs.Register(job, nil) 751 if err != nil { 752 t.Fatalf("err: %s", err) 753 } 754 if resp == nil || resp.EvalID == "" { 755 t.Fatalf("missing eval id") 756 } 757 assertWriteMeta(t, wm) 758 759 // Fail revert at incorrect enforce 760 _, _, err = jobs.Revert(*job.ID, 0, helper.Uint64ToPtr(10), nil) 761 if err == nil || !strings.Contains(err.Error(), "enforcing version") { 762 t.Fatalf("expected enforcement error: %v", err) 763 } 764 765 // Works at correct index 766 revertResp, wm, err := jobs.Revert(*job.ID, 0, helper.Uint64ToPtr(1), nil) 767 if err != nil { 768 t.Fatalf("err: %s", err) 769 } 770 if revertResp.EvalID == "" { 771 t.Fatalf("missing eval id") 772 } 773 if revertResp.EvalCreateIndex == 0 { 774 t.Fatalf("bad eval create index") 775 } 776 if revertResp.JobModifyIndex == 0 { 777 t.Fatalf("bad job modify index") 778 } 779 assertWriteMeta(t, wm) 780 } 781 782 func TestJobs_Info(t *testing.T) { 783 t.Parallel() 784 c, s := makeClient(t, nil, nil) 785 defer s.Stop() 786 jobs := c.Jobs() 787 788 // Trying to retrieve a job by ID before it exists 789 // returns an error 790 _, _, err := jobs.Info("job1", nil) 791 if err == nil || !strings.Contains(err.Error(), "not found") { 792 t.Fatalf("expected not found error, got: %#v", err) 793 } 794 795 // Register the job 796 job := testJob() 797 _, wm, err := jobs.Register(job, nil) 798 if err != nil { 799 t.Fatalf("err: %s", err) 800 } 801 assertWriteMeta(t, wm) 802 803 // Query the job again and ensure it exists 804 result, qm, err := jobs.Info("job1", nil) 805 if err != nil { 806 t.Fatalf("err: %s", err) 807 } 808 assertQueryMeta(t, qm) 809 810 // Check that the result is what we expect 811 if result == nil || *result.ID != *job.ID { 812 t.Fatalf("expect: %#v, got: %#v", job, result) 813 } 814 } 815 816 func TestJobs_Versions(t *testing.T) { 817 t.Parallel() 818 c, s := makeClient(t, nil, nil) 819 defer s.Stop() 820 jobs := c.Jobs() 821 822 // Trying to retrieve a job by ID before it exists returns an error 823 _, _, _, err := jobs.Versions("job1", false, nil) 824 if err == nil || !strings.Contains(err.Error(), "not found") { 825 t.Fatalf("expected not found error, got: %#v", err) 826 } 827 828 // Register the job 829 job := testJob() 830 _, wm, err := jobs.Register(job, nil) 831 if err != nil { 832 t.Fatalf("err: %s", err) 833 } 834 assertWriteMeta(t, wm) 835 836 // Query the job again and ensure it exists 837 result, _, qm, err := jobs.Versions("job1", false, nil) 838 if err != nil { 839 t.Fatalf("err: %s", err) 840 } 841 assertQueryMeta(t, qm) 842 843 // Check that the result is what we expect 844 if len(result) == 0 || *result[0].ID != *job.ID { 845 t.Fatalf("expect: %#v, got: %#v", job, result) 846 } 847 } 848 849 func TestJobs_PrefixList(t *testing.T) { 850 t.Parallel() 851 c, s := makeClient(t, nil, nil) 852 defer s.Stop() 853 jobs := c.Jobs() 854 855 // Listing when nothing exists returns empty 856 results, _, err := jobs.PrefixList("dummy") 857 if err != nil { 858 t.Fatalf("err: %s", err) 859 } 860 if n := len(results); n != 0 { 861 t.Fatalf("expected 0 jobs, got: %d", n) 862 } 863 864 // Register the job 865 job := testJob() 866 _, wm, err := jobs.Register(job, nil) 867 if err != nil { 868 t.Fatalf("err: %s", err) 869 } 870 assertWriteMeta(t, wm) 871 872 // Query the job again and ensure it exists 873 // Listing when nothing exists returns empty 874 results, _, err = jobs.PrefixList((*job.ID)[:1]) 875 if err != nil { 876 t.Fatalf("err: %s", err) 877 } 878 879 // Check if we have the right list 880 if len(results) != 1 || results[0].ID != *job.ID { 881 t.Fatalf("bad: %#v", results) 882 } 883 } 884 885 func TestJobs_List(t *testing.T) { 886 t.Parallel() 887 c, s := makeClient(t, nil, nil) 888 defer s.Stop() 889 jobs := c.Jobs() 890 891 // Listing when nothing exists returns empty 892 results, _, err := jobs.List(nil) 893 if err != nil { 894 t.Fatalf("err: %s", err) 895 } 896 if n := len(results); n != 0 { 897 t.Fatalf("expected 0 jobs, got: %d", n) 898 } 899 900 // Register the job 901 job := testJob() 902 _, wm, err := jobs.Register(job, nil) 903 if err != nil { 904 t.Fatalf("err: %s", err) 905 } 906 assertWriteMeta(t, wm) 907 908 // Query the job again and ensure it exists 909 // Listing when nothing exists returns empty 910 results, _, err = jobs.List(nil) 911 if err != nil { 912 t.Fatalf("err: %s", err) 913 } 914 915 // Check if we have the right list 916 if len(results) != 1 || results[0].ID != *job.ID { 917 t.Fatalf("bad: %#v", results) 918 } 919 } 920 921 func TestJobs_Allocations(t *testing.T) { 922 t.Parallel() 923 c, s := makeClient(t, nil, nil) 924 defer s.Stop() 925 jobs := c.Jobs() 926 927 // Looking up by a nonexistent job returns nothing 928 allocs, qm, err := jobs.Allocations("job1", true, nil) 929 if err != nil { 930 t.Fatalf("err: %s", err) 931 } 932 if qm.LastIndex != 0 { 933 t.Fatalf("bad index: %d", qm.LastIndex) 934 } 935 if n := len(allocs); n != 0 { 936 t.Fatalf("expected 0 allocs, got: %d", n) 937 } 938 939 // TODO: do something here to create some allocations for 940 // an existing job, lookup again. 941 } 942 943 func TestJobs_Evaluations(t *testing.T) { 944 t.Parallel() 945 c, s := makeClient(t, nil, nil) 946 defer s.Stop() 947 jobs := c.Jobs() 948 949 // Looking up by a nonexistent job ID returns nothing 950 evals, qm, err := jobs.Evaluations("job1", nil) 951 if err != nil { 952 t.Fatalf("err: %s", err) 953 } 954 if qm.LastIndex != 0 { 955 t.Fatalf("bad index: %d", qm.LastIndex) 956 } 957 if n := len(evals); n != 0 { 958 t.Fatalf("expected 0 evals, got: %d", n) 959 } 960 961 // Insert a job. This also creates an evaluation so we should 962 // be able to query that out after. 963 job := testJob() 964 resp, wm, err := jobs.Register(job, nil) 965 if err != nil { 966 t.Fatalf("err: %s", err) 967 } 968 assertWriteMeta(t, wm) 969 970 // Look up the evaluations again. 971 evals, qm, err = jobs.Evaluations("job1", nil) 972 if err != nil { 973 t.Fatalf("err: %s", err) 974 } 975 assertQueryMeta(t, qm) 976 977 // Check that we got the evals back, evals are in order most recent to least recent 978 // so the last eval is the original registered eval 979 idx := len(evals) - 1 980 if n := len(evals); n == 0 || evals[idx].ID != resp.EvalID { 981 t.Fatalf("expected >= 1 eval (%s), got: %#v", resp.EvalID, evals[idx]) 982 } 983 } 984 985 func TestJobs_Deregister(t *testing.T) { 986 t.Parallel() 987 c, s := makeClient(t, nil, nil) 988 defer s.Stop() 989 jobs := c.Jobs() 990 991 // Register a new job 992 job := testJob() 993 _, wm, err := jobs.Register(job, nil) 994 if err != nil { 995 t.Fatalf("err: %s", err) 996 } 997 assertWriteMeta(t, wm) 998 999 // Attempting delete on non-existing job returns an error 1000 if _, _, err = jobs.Deregister("nope", false, nil); err != nil { 1001 t.Fatalf("unexpected error deregistering job: %v", err) 1002 } 1003 1004 // Do a soft deregister of an existing job 1005 evalID, wm3, err := jobs.Deregister("job1", false, nil) 1006 if err != nil { 1007 t.Fatalf("err: %s", err) 1008 } 1009 assertWriteMeta(t, wm3) 1010 if evalID == "" { 1011 t.Fatalf("missing eval ID") 1012 } 1013 1014 // Check that the job is still queryable 1015 out, qm1, err := jobs.Info("job1", nil) 1016 if err != nil { 1017 t.Fatalf("err: %s", err) 1018 } 1019 assertQueryMeta(t, qm1) 1020 if out == nil { 1021 t.Fatalf("missing job") 1022 } 1023 1024 // Do a purge deregister of an existing job 1025 evalID, wm4, err := jobs.Deregister("job1", true, nil) 1026 if err != nil { 1027 t.Fatalf("err: %s", err) 1028 } 1029 assertWriteMeta(t, wm4) 1030 if evalID == "" { 1031 t.Fatalf("missing eval ID") 1032 } 1033 1034 // Check that the job is really gone 1035 result, qm, err := jobs.List(nil) 1036 if err != nil { 1037 t.Fatalf("err: %s", err) 1038 } 1039 assertQueryMeta(t, qm) 1040 if n := len(result); n != 0 { 1041 t.Fatalf("expected 0 jobs, got: %d", n) 1042 } 1043 } 1044 1045 func TestJobs_ForceEvaluate(t *testing.T) { 1046 t.Parallel() 1047 c, s := makeClient(t, nil, nil) 1048 defer s.Stop() 1049 jobs := c.Jobs() 1050 1051 // Force-eval on a non-existent job fails 1052 _, _, err := jobs.ForceEvaluate("job1", nil) 1053 if err == nil || !strings.Contains(err.Error(), "not found") { 1054 t.Fatalf("expected not found error, got: %#v", err) 1055 } 1056 1057 // Create a new job 1058 _, wm, err := jobs.Register(testJob(), nil) 1059 if err != nil { 1060 t.Fatalf("err: %s", err) 1061 } 1062 assertWriteMeta(t, wm) 1063 1064 // Try force-eval again 1065 evalID, wm, err := jobs.ForceEvaluate("job1", nil) 1066 if err != nil { 1067 t.Fatalf("err: %s", err) 1068 } 1069 assertWriteMeta(t, wm) 1070 1071 // Retrieve the evals and see if we get a matching one 1072 evals, qm, err := jobs.Evaluations("job1", nil) 1073 if err != nil { 1074 t.Fatalf("err: %s", err) 1075 } 1076 assertQueryMeta(t, qm) 1077 for _, eval := range evals { 1078 if eval.ID == evalID { 1079 return 1080 } 1081 } 1082 t.Fatalf("evaluation %q missing", evalID) 1083 } 1084 1085 func TestJobs_PeriodicForce(t *testing.T) { 1086 t.Parallel() 1087 c, s := makeClient(t, nil, nil) 1088 defer s.Stop() 1089 jobs := c.Jobs() 1090 1091 // Force-eval on a nonexistent job fails 1092 _, _, err := jobs.PeriodicForce("job1", nil) 1093 if err == nil || !strings.Contains(err.Error(), "not found") { 1094 t.Fatalf("expected not found error, got: %#v", err) 1095 } 1096 1097 // Create a new job 1098 job := testPeriodicJob() 1099 _, _, err = jobs.Register(job, nil) 1100 if err != nil { 1101 t.Fatalf("err: %s", err) 1102 } 1103 1104 testutil.WaitForResult(func() (bool, error) { 1105 out, _, err := jobs.Info(*job.ID, nil) 1106 if err != nil || out == nil || *out.ID != *job.ID { 1107 return false, err 1108 } 1109 return true, nil 1110 }, func(err error) { 1111 t.Fatalf("err: %s", err) 1112 }) 1113 1114 // Try force again 1115 evalID, wm, err := jobs.PeriodicForce(*job.ID, nil) 1116 if err != nil { 1117 t.Fatalf("err: %s", err) 1118 } 1119 assertWriteMeta(t, wm) 1120 1121 if evalID == "" { 1122 t.Fatalf("empty evalID") 1123 } 1124 1125 // Retrieve the eval 1126 evals := c.Evaluations() 1127 eval, qm, err := evals.Info(evalID, nil) 1128 if err != nil { 1129 t.Fatalf("err: %s", err) 1130 } 1131 assertQueryMeta(t, qm) 1132 if eval.ID == evalID { 1133 return 1134 } 1135 t.Fatalf("evaluation %q missing", evalID) 1136 } 1137 1138 func TestJobs_Plan(t *testing.T) { 1139 t.Parallel() 1140 c, s := makeClient(t, nil, nil) 1141 defer s.Stop() 1142 jobs := c.Jobs() 1143 1144 // Create a job and attempt to register it 1145 job := testJob() 1146 resp, wm, err := jobs.Register(job, nil) 1147 if err != nil { 1148 t.Fatalf("err: %s", err) 1149 } 1150 if resp == nil || resp.EvalID == "" { 1151 t.Fatalf("missing eval id") 1152 } 1153 assertWriteMeta(t, wm) 1154 1155 // Check that passing a nil job fails 1156 if _, _, err := jobs.Plan(nil, true, nil); err == nil { 1157 t.Fatalf("expect an error when job isn't provided") 1158 } 1159 1160 // Make a plan request 1161 planResp, wm, err := jobs.Plan(job, true, nil) 1162 if err != nil { 1163 t.Fatalf("err: %s", err) 1164 } 1165 if planResp == nil { 1166 t.Fatalf("nil response") 1167 } 1168 1169 if planResp.JobModifyIndex == 0 { 1170 t.Fatalf("bad JobModifyIndex value: %#v", planResp) 1171 } 1172 if planResp.Diff == nil { 1173 t.Fatalf("got nil diff: %#v", planResp) 1174 } 1175 if planResp.Annotations == nil { 1176 t.Fatalf("got nil annotations: %#v", planResp) 1177 } 1178 // Can make this assertion because there are no clients. 1179 if len(planResp.CreatedEvals) == 0 { 1180 t.Fatalf("got no CreatedEvals: %#v", planResp) 1181 } 1182 assertWriteMeta(t, wm) 1183 1184 // Make a plan request w/o the diff 1185 planResp, wm, err = jobs.Plan(job, false, nil) 1186 if err != nil { 1187 t.Fatalf("err: %s", err) 1188 } 1189 assertWriteMeta(t, wm) 1190 1191 if planResp == nil { 1192 t.Fatalf("nil response") 1193 } 1194 1195 if planResp.JobModifyIndex == 0 { 1196 t.Fatalf("bad JobModifyIndex value: %d", planResp.JobModifyIndex) 1197 } 1198 if planResp.Diff != nil { 1199 t.Fatalf("got non-nil diff: %#v", planResp) 1200 } 1201 if planResp.Annotations == nil { 1202 t.Fatalf("got nil annotations: %#v", planResp) 1203 } 1204 // Can make this assertion because there are no clients. 1205 if len(planResp.CreatedEvals) == 0 { 1206 t.Fatalf("got no CreatedEvals: %#v", planResp) 1207 } 1208 } 1209 1210 func TestJobs_JobSummary(t *testing.T) { 1211 t.Parallel() 1212 c, s := makeClient(t, nil, nil) 1213 defer s.Stop() 1214 jobs := c.Jobs() 1215 1216 // Trying to retrieve a job summary before the job exists 1217 // returns an error 1218 _, _, err := jobs.Summary("job1", nil) 1219 if err == nil || !strings.Contains(err.Error(), "not found") { 1220 t.Fatalf("expected not found error, got: %#v", err) 1221 } 1222 1223 // Register the job 1224 job := testJob() 1225 taskName := job.TaskGroups[0].Name 1226 _, wm, err := jobs.Register(job, nil) 1227 if err != nil { 1228 t.Fatalf("err: %s", err) 1229 } 1230 assertWriteMeta(t, wm) 1231 1232 // Query the job summary again and ensure it exists 1233 result, qm, err := jobs.Summary("job1", nil) 1234 if err != nil { 1235 t.Fatalf("err: %s", err) 1236 } 1237 assertQueryMeta(t, qm) 1238 1239 // Check that the result is what we expect 1240 if *job.ID != result.JobID { 1241 t.Fatalf("err: expected job id of %s saw %s", *job.ID, result.JobID) 1242 } 1243 if _, ok := result.Summary[*taskName]; !ok { 1244 t.Fatalf("err: unable to find %s key in job summary", *taskName) 1245 } 1246 } 1247 1248 func TestJobs_NewBatchJob(t *testing.T) { 1249 t.Parallel() 1250 job := NewBatchJob("job1", "myjob", "region1", 5) 1251 expect := &Job{ 1252 Region: helper.StringToPtr("region1"), 1253 ID: helper.StringToPtr("job1"), 1254 Name: helper.StringToPtr("myjob"), 1255 Type: helper.StringToPtr(JobTypeBatch), 1256 Priority: helper.IntToPtr(5), 1257 } 1258 if !reflect.DeepEqual(job, expect) { 1259 t.Fatalf("expect: %#v, got: %#v", expect, job) 1260 } 1261 } 1262 1263 func TestJobs_NewServiceJob(t *testing.T) { 1264 t.Parallel() 1265 job := NewServiceJob("job1", "myjob", "region1", 5) 1266 expect := &Job{ 1267 Region: helper.StringToPtr("region1"), 1268 ID: helper.StringToPtr("job1"), 1269 Name: helper.StringToPtr("myjob"), 1270 Type: helper.StringToPtr(JobTypeService), 1271 Priority: helper.IntToPtr(5), 1272 } 1273 if !reflect.DeepEqual(job, expect) { 1274 t.Fatalf("expect: %#v, got: %#v", expect, job) 1275 } 1276 } 1277 1278 func TestJobs_SetMeta(t *testing.T) { 1279 t.Parallel() 1280 job := &Job{Meta: nil} 1281 1282 // Initializes a nil map 1283 out := job.SetMeta("foo", "bar") 1284 if job.Meta == nil { 1285 t.Fatalf("should initialize metadata") 1286 } 1287 1288 // Check that the job was returned 1289 if job != out { 1290 t.Fatalf("expect: %#v, got: %#v", job, out) 1291 } 1292 1293 // Setting another pair is additive 1294 job.SetMeta("baz", "zip") 1295 expect := map[string]string{"foo": "bar", "baz": "zip"} 1296 if !reflect.DeepEqual(job.Meta, expect) { 1297 t.Fatalf("expect: %#v, got: %#v", expect, job.Meta) 1298 } 1299 } 1300 1301 func TestJobs_Constrain(t *testing.T) { 1302 t.Parallel() 1303 job := &Job{Constraints: nil} 1304 1305 // Create and add a constraint 1306 out := job.Constrain(NewConstraint("kernel.name", "=", "darwin")) 1307 if n := len(job.Constraints); n != 1 { 1308 t.Fatalf("expected 1 constraint, got: %d", n) 1309 } 1310 1311 // Check that the job was returned 1312 if job != out { 1313 t.Fatalf("expect: %#v, got: %#v", job, out) 1314 } 1315 1316 // Adding another constraint preserves the original 1317 job.Constrain(NewConstraint("memory.totalbytes", ">=", "128000000")) 1318 expect := []*Constraint{ 1319 { 1320 LTarget: "kernel.name", 1321 RTarget: "darwin", 1322 Operand: "=", 1323 }, 1324 { 1325 LTarget: "memory.totalbytes", 1326 RTarget: "128000000", 1327 Operand: ">=", 1328 }, 1329 } 1330 if !reflect.DeepEqual(job.Constraints, expect) { 1331 t.Fatalf("expect: %#v, got: %#v", expect, job.Constraints) 1332 } 1333 } 1334 1335 func TestJobs_Sort(t *testing.T) { 1336 t.Parallel() 1337 jobs := []*JobListStub{ 1338 {ID: "job2"}, 1339 {ID: "job0"}, 1340 {ID: "job1"}, 1341 } 1342 sort.Sort(JobIDSort(jobs)) 1343 1344 expect := []*JobListStub{ 1345 {ID: "job0"}, 1346 {ID: "job1"}, 1347 {ID: "job2"}, 1348 } 1349 if !reflect.DeepEqual(jobs, expect) { 1350 t.Fatalf("\n\n%#v\n\n%#v", jobs, expect) 1351 } 1352 } 1353 1354 func TestJobs_Summary_WithACL(t *testing.T) { 1355 t.Parallel() 1356 assert := assert.New(t) 1357 1358 c, s, root := makeACLClient(t, nil, nil) 1359 defer s.Stop() 1360 jobs := c.Jobs() 1361 1362 invalidToken := mock.ACLToken() 1363 1364 // Registering with an invalid token should fail 1365 c.SetSecretID(invalidToken.SecretID) 1366 job := testJob() 1367 _, _, err := jobs.Register(job, nil) 1368 assert.NotNil(err) 1369 1370 // Register with token should succeed 1371 c.SetSecretID(root.SecretID) 1372 resp2, wm, err := jobs.Register(job, nil) 1373 assert.Nil(err) 1374 assert.NotNil(resp2) 1375 assert.NotEqual("", resp2.EvalID) 1376 assertWriteMeta(t, wm) 1377 1378 // Query the job summary with an invalid token should fail 1379 c.SetSecretID(invalidToken.SecretID) 1380 result, _, err := jobs.Summary(*job.ID, nil) 1381 assert.NotNil(err) 1382 1383 // Query the job summary with a valid token should succeed 1384 c.SetSecretID(root.SecretID) 1385 result, qm, err := jobs.Summary(*job.ID, nil) 1386 assert.Nil(err) 1387 assertQueryMeta(t, qm) 1388 1389 // Check that the result is what we expect 1390 assert.Equal(*job.ID, result.JobID) 1391 }