github.com/hooklift/nomad@v0.5.7-0.20170407200202-db11e7dd7b55/command/agent/job_endpoint_test.go (about) 1 package agent 2 3 import ( 4 "net/http" 5 "net/http/httptest" 6 "reflect" 7 "testing" 8 "time" 9 10 "github.com/golang/snappy" 11 "github.com/hashicorp/nomad/api" 12 "github.com/hashicorp/nomad/helper" 13 "github.com/hashicorp/nomad/nomad/mock" 14 "github.com/hashicorp/nomad/nomad/structs" 15 ) 16 17 func TestHTTP_JobsList(t *testing.T) { 18 httpTest(t, nil, func(s *TestServer) { 19 for i := 0; i < 3; i++ { 20 // Create the job 21 job := mock.Job() 22 args := structs.JobRegisterRequest{ 23 Job: job, 24 WriteRequest: structs.WriteRequest{Region: "global"}, 25 } 26 var resp structs.JobRegisterResponse 27 if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { 28 t.Fatalf("err: %v", err) 29 } 30 } 31 32 // Make the HTTP request 33 req, err := http.NewRequest("GET", "/v1/jobs", nil) 34 if err != nil { 35 t.Fatalf("err: %v", err) 36 } 37 respW := httptest.NewRecorder() 38 39 // Make the request 40 obj, err := s.Server.JobsRequest(respW, req) 41 if err != nil { 42 t.Fatalf("err: %v", err) 43 } 44 45 // Check for the index 46 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 47 t.Fatalf("missing index") 48 } 49 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 50 t.Fatalf("missing known leader") 51 } 52 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 53 t.Fatalf("missing last contact") 54 } 55 56 // Check the job 57 j := obj.([]*structs.JobListStub) 58 if len(j) != 3 { 59 t.Fatalf("bad: %#v", j) 60 } 61 }) 62 } 63 64 func TestHTTP_PrefixJobsList(t *testing.T) { 65 ids := []string{ 66 "aaaaaaaa-e8f7-fd38-c855-ab94ceb89706", 67 "aabbbbbb-e8f7-fd38-c855-ab94ceb89706", 68 "aabbcccc-e8f7-fd38-c855-ab94ceb89706", 69 } 70 httpTest(t, nil, func(s *TestServer) { 71 for i := 0; i < 3; i++ { 72 // Create the job 73 job := mock.Job() 74 job.ID = ids[i] 75 job.TaskGroups[0].Count = 1 76 args := structs.JobRegisterRequest{ 77 Job: job, 78 WriteRequest: structs.WriteRequest{Region: "global"}, 79 } 80 var resp structs.JobRegisterResponse 81 if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { 82 t.Fatalf("err: %v", err) 83 } 84 } 85 86 // Make the HTTP request 87 req, err := http.NewRequest("GET", "/v1/jobs?prefix=aabb", nil) 88 if err != nil { 89 t.Fatalf("err: %v", err) 90 } 91 respW := httptest.NewRecorder() 92 93 // Make the request 94 obj, err := s.Server.JobsRequest(respW, req) 95 if err != nil { 96 t.Fatalf("err: %v", err) 97 } 98 99 // Check for the index 100 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 101 t.Fatalf("missing index") 102 } 103 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 104 t.Fatalf("missing known leader") 105 } 106 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 107 t.Fatalf("missing last contact") 108 } 109 110 // Check the job 111 j := obj.([]*structs.JobListStub) 112 if len(j) != 2 { 113 t.Fatalf("bad: %#v", j) 114 } 115 }) 116 } 117 118 func TestHTTP_JobsRegister(t *testing.T) { 119 httpTest(t, nil, func(s *TestServer) { 120 // Create the job 121 job := api.MockJob() 122 args := api.JobRegisterRequest{ 123 Job: job, 124 WriteRequest: api.WriteRequest{Region: "global"}, 125 } 126 buf := encodeReq(args) 127 128 // Make the HTTP request 129 req, err := http.NewRequest("PUT", "/v1/jobs", buf) 130 if err != nil { 131 t.Fatalf("err: %v", err) 132 } 133 respW := httptest.NewRecorder() 134 135 // Make the request 136 obj, err := s.Server.JobsRequest(respW, req) 137 if err != nil { 138 t.Fatalf("err: %v", err) 139 } 140 141 // Check the response 142 dereg := obj.(structs.JobRegisterResponse) 143 if dereg.EvalID == "" { 144 t.Fatalf("bad: %v", dereg) 145 } 146 147 // Check for the index 148 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 149 t.Fatalf("missing index") 150 } 151 152 // Check the job is registered 153 getReq := structs.JobSpecificRequest{ 154 JobID: *job.ID, 155 QueryOptions: structs.QueryOptions{Region: "global"}, 156 } 157 var getResp structs.SingleJobResponse 158 if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil { 159 t.Fatalf("err: %v", err) 160 } 161 162 if getResp.Job == nil { 163 t.Fatalf("job does not exist") 164 } 165 }) 166 } 167 168 func TestHTTP_JobsRegister_Defaulting(t *testing.T) { 169 httpTest(t, nil, func(s *TestServer) { 170 // Create the job 171 job := api.MockJob() 172 173 // Do not set its priority 174 job.Priority = nil 175 176 args := api.JobRegisterRequest{ 177 Job: job, 178 WriteRequest: api.WriteRequest{Region: "global"}, 179 } 180 buf := encodeReq(args) 181 182 // Make the HTTP request 183 req, err := http.NewRequest("PUT", "/v1/jobs", buf) 184 if err != nil { 185 t.Fatalf("err: %v", err) 186 } 187 respW := httptest.NewRecorder() 188 189 // Make the request 190 obj, err := s.Server.JobsRequest(respW, req) 191 if err != nil { 192 t.Fatalf("err: %v", err) 193 } 194 195 // Check the response 196 dereg := obj.(structs.JobRegisterResponse) 197 if dereg.EvalID == "" { 198 t.Fatalf("bad: %v", dereg) 199 } 200 201 // Check for the index 202 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 203 t.Fatalf("missing index") 204 } 205 206 // Check the job is registered 207 getReq := structs.JobSpecificRequest{ 208 JobID: *job.ID, 209 QueryOptions: structs.QueryOptions{Region: "global"}, 210 } 211 var getResp structs.SingleJobResponse 212 if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil { 213 t.Fatalf("err: %v", err) 214 } 215 216 if getResp.Job == nil { 217 t.Fatalf("job does not exist") 218 } 219 if getResp.Job.Priority != 50 { 220 t.Fatalf("job didn't get defaulted") 221 } 222 }) 223 } 224 225 func TestHTTP_JobQuery(t *testing.T) { 226 httpTest(t, nil, func(s *TestServer) { 227 // Create the job 228 job := mock.Job() 229 args := structs.JobRegisterRequest{ 230 Job: job, 231 WriteRequest: structs.WriteRequest{Region: "global"}, 232 } 233 var resp structs.JobRegisterResponse 234 if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { 235 t.Fatalf("err: %v", err) 236 } 237 238 // Make the HTTP request 239 req, err := http.NewRequest("GET", "/v1/job/"+job.ID, nil) 240 if err != nil { 241 t.Fatalf("err: %v", err) 242 } 243 respW := httptest.NewRecorder() 244 245 // Make the request 246 obj, err := s.Server.JobSpecificRequest(respW, req) 247 if err != nil { 248 t.Fatalf("err: %v", err) 249 } 250 251 // Check for the index 252 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 253 t.Fatalf("missing index") 254 } 255 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 256 t.Fatalf("missing known leader") 257 } 258 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 259 t.Fatalf("missing last contact") 260 } 261 262 // Check the job 263 j := obj.(*structs.Job) 264 if j.ID != job.ID { 265 t.Fatalf("bad: %#v", j) 266 } 267 }) 268 } 269 270 func TestHTTP_JobQuery_Payload(t *testing.T) { 271 httpTest(t, nil, func(s *TestServer) { 272 // Create the job 273 job := mock.Job() 274 275 // Insert Payload compressed 276 expected := []byte("hello world") 277 compressed := snappy.Encode(nil, expected) 278 job.Payload = compressed 279 280 // Directly manipulate the state 281 state := s.Agent.server.State() 282 if err := state.UpsertJob(1000, job); err != nil { 283 t.Fatalf("Failed to upsert job: %v", err) 284 } 285 286 // Make the HTTP request 287 req, err := http.NewRequest("GET", "/v1/job/"+job.ID, nil) 288 if err != nil { 289 t.Fatalf("err: %v", err) 290 } 291 respW := httptest.NewRecorder() 292 293 // Make the request 294 obj, err := s.Server.JobSpecificRequest(respW, req) 295 if err != nil { 296 t.Fatalf("err: %v", err) 297 } 298 299 // Check for the index 300 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 301 t.Fatalf("missing index") 302 } 303 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 304 t.Fatalf("missing known leader") 305 } 306 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 307 t.Fatalf("missing last contact") 308 } 309 310 // Check the job 311 j := obj.(*structs.Job) 312 if j.ID != job.ID { 313 t.Fatalf("bad: %#v", j) 314 } 315 316 // Check the payload is decompressed 317 if !reflect.DeepEqual(j.Payload, expected) { 318 t.Fatalf("Payload not decompressed properly; got %#v; want %#v", j.Payload, expected) 319 } 320 }) 321 } 322 323 func TestHTTP_JobUpdate(t *testing.T) { 324 httpTest(t, nil, func(s *TestServer) { 325 // Create the job 326 job := api.MockJob() 327 args := api.JobRegisterRequest{ 328 Job: job, 329 WriteRequest: api.WriteRequest{Region: "global"}, 330 } 331 buf := encodeReq(args) 332 333 // Make the HTTP request 334 req, err := http.NewRequest("PUT", "/v1/job/"+*job.ID, buf) 335 if err != nil { 336 t.Fatalf("err: %v", err) 337 } 338 respW := httptest.NewRecorder() 339 340 // Make the request 341 obj, err := s.Server.JobSpecificRequest(respW, req) 342 if err != nil { 343 t.Fatalf("err: %v", err) 344 } 345 346 // Check the response 347 dereg := obj.(structs.JobRegisterResponse) 348 if dereg.EvalID == "" { 349 t.Fatalf("bad: %v", dereg) 350 } 351 352 // Check for the index 353 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 354 t.Fatalf("missing index") 355 } 356 357 // Check the job is registered 358 getReq := structs.JobSpecificRequest{ 359 JobID: *job.ID, 360 QueryOptions: structs.QueryOptions{Region: "global"}, 361 } 362 var getResp structs.SingleJobResponse 363 if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil { 364 t.Fatalf("err: %v", err) 365 } 366 367 if getResp.Job == nil { 368 t.Fatalf("job does not exist") 369 } 370 }) 371 } 372 373 func TestHTTP_JobDelete(t *testing.T) { 374 httpTest(t, nil, func(s *TestServer) { 375 // Create the job 376 job := mock.Job() 377 args := structs.JobRegisterRequest{ 378 Job: job, 379 WriteRequest: structs.WriteRequest{Region: "global"}, 380 } 381 var resp structs.JobRegisterResponse 382 if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { 383 t.Fatalf("err: %v", err) 384 } 385 386 // Make the HTTP request 387 req, err := http.NewRequest("DELETE", "/v1/job/"+job.ID, nil) 388 if err != nil { 389 t.Fatalf("err: %v", err) 390 } 391 respW := httptest.NewRecorder() 392 393 // Make the request 394 obj, err := s.Server.JobSpecificRequest(respW, req) 395 if err != nil { 396 t.Fatalf("err: %v", err) 397 } 398 399 // Check the response 400 dereg := obj.(structs.JobDeregisterResponse) 401 if dereg.EvalID == "" { 402 t.Fatalf("bad: %v", dereg) 403 } 404 405 // Check for the index 406 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 407 t.Fatalf("missing index") 408 } 409 410 // Check the job is gone 411 getReq := structs.JobSpecificRequest{ 412 JobID: job.ID, 413 QueryOptions: structs.QueryOptions{Region: "global"}, 414 } 415 var getResp structs.SingleJobResponse 416 if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil { 417 t.Fatalf("err: %v", err) 418 } 419 if getResp.Job != nil { 420 t.Fatalf("job still exists") 421 } 422 }) 423 } 424 425 func TestHTTP_JobForceEvaluate(t *testing.T) { 426 httpTest(t, nil, func(s *TestServer) { 427 // Create the job 428 job := mock.Job() 429 args := structs.JobRegisterRequest{ 430 Job: job, 431 WriteRequest: structs.WriteRequest{Region: "global"}, 432 } 433 var resp structs.JobRegisterResponse 434 if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { 435 t.Fatalf("err: %v", err) 436 } 437 438 // Make the HTTP request 439 req, err := http.NewRequest("POST", "/v1/job/"+job.ID+"/evaluate", nil) 440 if err != nil { 441 t.Fatalf("err: %v", err) 442 } 443 respW := httptest.NewRecorder() 444 445 // Make the request 446 obj, err := s.Server.JobSpecificRequest(respW, req) 447 if err != nil { 448 t.Fatalf("err: %v", err) 449 } 450 451 // Check the response 452 reg := obj.(structs.JobRegisterResponse) 453 if reg.EvalID == "" { 454 t.Fatalf("bad: %v", reg) 455 } 456 457 // Check for the index 458 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 459 t.Fatalf("missing index") 460 } 461 }) 462 } 463 464 func TestHTTP_JobEvaluations(t *testing.T) { 465 httpTest(t, nil, func(s *TestServer) { 466 // Create the job 467 job := mock.Job() 468 args := structs.JobRegisterRequest{ 469 Job: job, 470 WriteRequest: structs.WriteRequest{Region: "global"}, 471 } 472 var resp structs.JobRegisterResponse 473 if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { 474 t.Fatalf("err: %v", err) 475 } 476 477 // Make the HTTP request 478 req, err := http.NewRequest("GET", "/v1/job/"+job.ID+"/evaluations", nil) 479 if err != nil { 480 t.Fatalf("err: %v", err) 481 } 482 respW := httptest.NewRecorder() 483 484 // Make the request 485 obj, err := s.Server.JobSpecificRequest(respW, req) 486 if err != nil { 487 t.Fatalf("err: %v", err) 488 } 489 490 // Check the response 491 evals := obj.([]*structs.Evaluation) 492 // Can be multiple evals, use the last one, since they are in order 493 idx := len(evals) - 1 494 if len(evals) < 0 || evals[idx].ID != resp.EvalID { 495 t.Fatalf("bad: %v", evals) 496 } 497 498 // Check for the index 499 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 500 t.Fatalf("missing index") 501 } 502 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 503 t.Fatalf("missing known leader") 504 } 505 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 506 t.Fatalf("missing last contact") 507 } 508 }) 509 } 510 511 func TestHTTP_JobAllocations(t *testing.T) { 512 httpTest(t, nil, func(s *TestServer) { 513 // Create the job 514 alloc1 := mock.Alloc() 515 args := structs.JobRegisterRequest{ 516 Job: alloc1.Job, 517 WriteRequest: structs.WriteRequest{Region: "global"}, 518 } 519 var resp structs.JobRegisterResponse 520 if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { 521 t.Fatalf("err: %v", err) 522 } 523 524 // Directly manipulate the state 525 state := s.Agent.server.State() 526 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) 527 if err != nil { 528 t.Fatalf("err: %v", err) 529 } 530 531 // Make the HTTP request 532 req, err := http.NewRequest("GET", "/v1/job/"+alloc1.Job.ID+"/allocations?all=true", nil) 533 if err != nil { 534 t.Fatalf("err: %v", err) 535 } 536 respW := httptest.NewRecorder() 537 538 // Make the request 539 obj, err := s.Server.JobSpecificRequest(respW, req) 540 if err != nil { 541 t.Fatalf("err: %v", err) 542 } 543 544 // Check the response 545 allocs := obj.([]*structs.AllocListStub) 546 if len(allocs) != 1 && allocs[0].ID != alloc1.ID { 547 t.Fatalf("bad: %v", allocs) 548 } 549 550 // Check for the index 551 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 552 t.Fatalf("missing index") 553 } 554 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 555 t.Fatalf("missing known leader") 556 } 557 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 558 t.Fatalf("missing last contact") 559 } 560 }) 561 } 562 563 func TestHTTP_PeriodicForce(t *testing.T) { 564 httpTest(t, nil, func(s *TestServer) { 565 // Create and register a periodic job. 566 job := mock.PeriodicJob() 567 args := structs.JobRegisterRequest{ 568 Job: job, 569 WriteRequest: structs.WriteRequest{Region: "global"}, 570 } 571 var resp structs.JobRegisterResponse 572 if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { 573 t.Fatalf("err: %v", err) 574 } 575 576 // Make the HTTP request 577 req, err := http.NewRequest("POST", "/v1/job/"+job.ID+"/periodic/force", nil) 578 if err != nil { 579 t.Fatalf("err: %v", err) 580 } 581 respW := httptest.NewRecorder() 582 583 // Make the request 584 obj, err := s.Server.JobSpecificRequest(respW, req) 585 if err != nil { 586 t.Fatalf("err: %v", err) 587 } 588 589 // Check for the index 590 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 591 t.Fatalf("missing index") 592 } 593 594 // Check the response 595 r := obj.(structs.PeriodicForceResponse) 596 if r.EvalID == "" { 597 t.Fatalf("bad: %#v", r) 598 } 599 }) 600 } 601 602 func TestHTTP_JobPlan(t *testing.T) { 603 httpTest(t, nil, func(s *TestServer) { 604 // Create the job 605 job := mock.Job() 606 args := structs.JobPlanRequest{ 607 Job: job, 608 Diff: true, 609 WriteRequest: structs.WriteRequest{Region: "global"}, 610 } 611 buf := encodeReq(args) 612 613 // Make the HTTP request 614 req, err := http.NewRequest("PUT", "/v1/job/"+job.ID+"/plan", buf) 615 if err != nil { 616 t.Fatalf("err: %v", err) 617 } 618 respW := httptest.NewRecorder() 619 620 // Make the request 621 obj, err := s.Server.JobSpecificRequest(respW, req) 622 if err != nil { 623 t.Fatalf("err: %v", err) 624 } 625 626 // Check the response 627 plan := obj.(structs.JobPlanResponse) 628 if plan.Annotations == nil { 629 t.Fatalf("bad: %v", plan) 630 } 631 632 if plan.Diff == nil { 633 t.Fatalf("bad: %v", plan) 634 } 635 }) 636 } 637 638 func TestHTTP_JobDispatch(t *testing.T) { 639 httpTest(t, nil, func(s *TestServer) { 640 // Create the parameterized job 641 job := mock.Job() 642 job.Type = "batch" 643 job.ParameterizedJob = &structs.ParameterizedJobConfig{} 644 645 args := structs.JobRegisterRequest{ 646 Job: job, 647 WriteRequest: structs.WriteRequest{Region: "global"}, 648 } 649 var resp structs.JobRegisterResponse 650 if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { 651 t.Fatalf("err: %v", err) 652 } 653 654 // Make the request 655 respW := httptest.NewRecorder() 656 args2 := structs.JobDispatchRequest{ 657 WriteRequest: structs.WriteRequest{Region: "global"}, 658 } 659 buf := encodeReq(args2) 660 661 // Make the HTTP request 662 req2, err := http.NewRequest("PUT", "/v1/job/"+job.ID+"/dispatch", buf) 663 if err != nil { 664 t.Fatalf("err: %v", err) 665 } 666 respW.Flush() 667 668 // Make the request 669 obj, err := s.Server.JobSpecificRequest(respW, req2) 670 if err != nil { 671 t.Fatalf("err: %v", err) 672 } 673 674 // Check the response 675 dispatch := obj.(structs.JobDispatchResponse) 676 if dispatch.EvalID == "" { 677 t.Fatalf("bad: %v", dispatch) 678 } 679 680 if dispatch.DispatchedJobID == "" { 681 t.Fatalf("bad: %v", dispatch) 682 } 683 }) 684 } 685 686 func TestJobs_ApiJobToStructsJob(t *testing.T) { 687 apiJob := &api.Job{ 688 Region: helper.StringToPtr("global"), 689 ID: helper.StringToPtr("foo"), 690 ParentID: helper.StringToPtr("lol"), 691 Name: helper.StringToPtr("name"), 692 Type: helper.StringToPtr("service"), 693 Priority: helper.IntToPtr(50), 694 AllAtOnce: helper.BoolToPtr(true), 695 Datacenters: []string{"dc1", "dc2"}, 696 Constraints: []*api.Constraint{ 697 { 698 LTarget: "a", 699 RTarget: "b", 700 Operand: "c", 701 }, 702 }, 703 Update: &api.UpdateStrategy{ 704 Stagger: 1 * time.Second, 705 MaxParallel: 5, 706 }, 707 Periodic: &api.PeriodicConfig{ 708 Enabled: helper.BoolToPtr(true), 709 Spec: helper.StringToPtr("spec"), 710 SpecType: helper.StringToPtr("cron"), 711 ProhibitOverlap: helper.BoolToPtr(true), 712 TimeZone: helper.StringToPtr("test zone"), 713 }, 714 ParameterizedJob: &api.ParameterizedJobConfig{ 715 Payload: "payload", 716 MetaRequired: []string{"a", "b"}, 717 MetaOptional: []string{"c", "d"}, 718 }, 719 Payload: []byte("payload"), 720 Meta: map[string]string{ 721 "foo": "bar", 722 }, 723 TaskGroups: []*api.TaskGroup{ 724 { 725 Name: helper.StringToPtr("group1"), 726 Count: helper.IntToPtr(5), 727 Constraints: []*api.Constraint{ 728 { 729 LTarget: "x", 730 RTarget: "y", 731 Operand: "z", 732 }, 733 }, 734 RestartPolicy: &api.RestartPolicy{ 735 Interval: helper.TimeToPtr(1 * time.Second), 736 Attempts: helper.IntToPtr(5), 737 Delay: helper.TimeToPtr(10 * time.Second), 738 Mode: helper.StringToPtr("delay"), 739 }, 740 EphemeralDisk: &api.EphemeralDisk{ 741 SizeMB: helper.IntToPtr(100), 742 Sticky: helper.BoolToPtr(true), 743 Migrate: helper.BoolToPtr(true), 744 }, 745 Meta: map[string]string{ 746 "key": "value", 747 }, 748 Tasks: []*api.Task{ 749 { 750 Name: "task1", 751 Leader: true, 752 Driver: "docker", 753 User: "mary", 754 Config: map[string]interface{}{ 755 "lol": "code", 756 }, 757 Env: map[string]string{ 758 "hello": "world", 759 }, 760 Constraints: []*api.Constraint{ 761 { 762 LTarget: "x", 763 RTarget: "y", 764 Operand: "z", 765 }, 766 }, 767 768 Services: []*api.Service{ 769 { 770 Id: "id", 771 Name: "serviceA", 772 Tags: []string{"1", "2"}, 773 PortLabel: "foo", 774 Checks: []api.ServiceCheck{ 775 { 776 Id: "hello", 777 Name: "bar", 778 Type: "http", 779 Command: "foo", 780 Args: []string{"a", "b"}, 781 Path: "/check", 782 Protocol: "http", 783 PortLabel: "foo", 784 Interval: 4 * time.Second, 785 Timeout: 2 * time.Second, 786 InitialStatus: "ok", 787 }, 788 }, 789 }, 790 }, 791 Resources: &api.Resources{ 792 CPU: helper.IntToPtr(100), 793 MemoryMB: helper.IntToPtr(10), 794 Networks: []*api.NetworkResource{ 795 { 796 IP: "10.10.11.1", 797 MBits: helper.IntToPtr(10), 798 ReservedPorts: []api.Port{ 799 { 800 Label: "http", 801 Value: 80, 802 }, 803 }, 804 DynamicPorts: []api.Port{ 805 { 806 Label: "ssh", 807 Value: 2000, 808 }, 809 }, 810 }, 811 }, 812 }, 813 Meta: map[string]string{ 814 "lol": "code", 815 }, 816 KillTimeout: helper.TimeToPtr(10 * time.Second), 817 LogConfig: &api.LogConfig{ 818 MaxFiles: helper.IntToPtr(10), 819 MaxFileSizeMB: helper.IntToPtr(100), 820 }, 821 Artifacts: []*api.TaskArtifact{ 822 { 823 GetterSource: helper.StringToPtr("source"), 824 GetterOptions: map[string]string{ 825 "a": "b", 826 }, 827 RelativeDest: helper.StringToPtr("dest"), 828 }, 829 }, 830 Vault: &api.Vault{ 831 Policies: []string{"a", "b", "c"}, 832 Env: helper.BoolToPtr(true), 833 ChangeMode: helper.StringToPtr("c"), 834 ChangeSignal: helper.StringToPtr("sighup"), 835 }, 836 Templates: []*api.Template{ 837 { 838 SourcePath: helper.StringToPtr("source"), 839 DestPath: helper.StringToPtr("dest"), 840 EmbeddedTmpl: helper.StringToPtr("embedded"), 841 ChangeMode: helper.StringToPtr("change"), 842 ChangeSignal: helper.StringToPtr("signal"), 843 Splay: helper.TimeToPtr(1 * time.Minute), 844 Perms: helper.StringToPtr("666"), 845 LeftDelim: helper.StringToPtr("abc"), 846 RightDelim: helper.StringToPtr("def"), 847 }, 848 }, 849 DispatchPayload: &api.DispatchPayloadConfig{ 850 File: "fileA", 851 }, 852 }, 853 }, 854 }, 855 }, 856 VaultToken: helper.StringToPtr("token"), 857 Status: helper.StringToPtr("status"), 858 StatusDescription: helper.StringToPtr("status_desc"), 859 CreateIndex: helper.Uint64ToPtr(1), 860 ModifyIndex: helper.Uint64ToPtr(3), 861 JobModifyIndex: helper.Uint64ToPtr(5), 862 } 863 864 expected := &structs.Job{ 865 Region: "global", 866 ID: "foo", 867 ParentID: "lol", 868 Name: "name", 869 Type: "service", 870 Priority: 50, 871 AllAtOnce: true, 872 Datacenters: []string{"dc1", "dc2"}, 873 Constraints: []*structs.Constraint{ 874 { 875 LTarget: "a", 876 RTarget: "b", 877 Operand: "c", 878 }, 879 }, 880 Update: structs.UpdateStrategy{ 881 Stagger: 1 * time.Second, 882 MaxParallel: 5, 883 }, 884 Periodic: &structs.PeriodicConfig{ 885 Enabled: true, 886 Spec: "spec", 887 SpecType: "cron", 888 ProhibitOverlap: true, 889 TimeZone: "test zone", 890 }, 891 ParameterizedJob: &structs.ParameterizedJobConfig{ 892 Payload: "payload", 893 MetaRequired: []string{"a", "b"}, 894 MetaOptional: []string{"c", "d"}, 895 }, 896 Payload: []byte("payload"), 897 Meta: map[string]string{ 898 "foo": "bar", 899 }, 900 TaskGroups: []*structs.TaskGroup{ 901 { 902 Name: "group1", 903 Count: 5, 904 Constraints: []*structs.Constraint{ 905 { 906 LTarget: "x", 907 RTarget: "y", 908 Operand: "z", 909 }, 910 }, 911 RestartPolicy: &structs.RestartPolicy{ 912 Interval: 1 * time.Second, 913 Attempts: 5, 914 Delay: 10 * time.Second, 915 Mode: "delay", 916 }, 917 EphemeralDisk: &structs.EphemeralDisk{ 918 SizeMB: 100, 919 Sticky: true, 920 Migrate: true, 921 }, 922 Meta: map[string]string{ 923 "key": "value", 924 }, 925 Tasks: []*structs.Task{ 926 { 927 Name: "task1", 928 Driver: "docker", 929 Leader: true, 930 User: "mary", 931 Config: map[string]interface{}{ 932 "lol": "code", 933 }, 934 Constraints: []*structs.Constraint{ 935 { 936 LTarget: "x", 937 RTarget: "y", 938 Operand: "z", 939 }, 940 }, 941 Env: map[string]string{ 942 "hello": "world", 943 }, 944 Services: []*structs.Service{ 945 &structs.Service{ 946 Name: "serviceA", 947 Tags: []string{"1", "2"}, 948 PortLabel: "foo", 949 Checks: []*structs.ServiceCheck{ 950 &structs.ServiceCheck{ 951 Name: "bar", 952 Type: "http", 953 Command: "foo", 954 Args: []string{"a", "b"}, 955 Path: "/check", 956 Protocol: "http", 957 PortLabel: "foo", 958 Interval: 4 * time.Second, 959 Timeout: 2 * time.Second, 960 InitialStatus: "ok", 961 }, 962 }, 963 }, 964 }, 965 Resources: &structs.Resources{ 966 CPU: 100, 967 MemoryMB: 10, 968 Networks: []*structs.NetworkResource{ 969 { 970 IP: "10.10.11.1", 971 MBits: 10, 972 ReservedPorts: []structs.Port{ 973 { 974 Label: "http", 975 Value: 80, 976 }, 977 }, 978 DynamicPorts: []structs.Port{ 979 { 980 Label: "ssh", 981 Value: 2000, 982 }, 983 }, 984 }, 985 }, 986 }, 987 Meta: map[string]string{ 988 "lol": "code", 989 }, 990 KillTimeout: 10 * time.Second, 991 LogConfig: &structs.LogConfig{ 992 MaxFiles: 10, 993 MaxFileSizeMB: 100, 994 }, 995 Artifacts: []*structs.TaskArtifact{ 996 { 997 GetterSource: "source", 998 GetterOptions: map[string]string{ 999 "a": "b", 1000 }, 1001 RelativeDest: "dest", 1002 }, 1003 }, 1004 Vault: &structs.Vault{ 1005 Policies: []string{"a", "b", "c"}, 1006 Env: true, 1007 ChangeMode: "c", 1008 ChangeSignal: "sighup", 1009 }, 1010 Templates: []*structs.Template{ 1011 { 1012 SourcePath: "source", 1013 DestPath: "dest", 1014 EmbeddedTmpl: "embedded", 1015 ChangeMode: "change", 1016 ChangeSignal: "SIGNAL", 1017 Splay: 1 * time.Minute, 1018 Perms: "666", 1019 LeftDelim: "abc", 1020 RightDelim: "def", 1021 }, 1022 }, 1023 DispatchPayload: &structs.DispatchPayloadConfig{ 1024 File: "fileA", 1025 }, 1026 }, 1027 }, 1028 }, 1029 }, 1030 1031 VaultToken: "token", 1032 Status: "status", 1033 StatusDescription: "status_desc", 1034 CreateIndex: 1, 1035 ModifyIndex: 3, 1036 JobModifyIndex: 5, 1037 } 1038 1039 structsJob := ApiJobToStructJob(apiJob) 1040 1041 if !reflect.DeepEqual(expected, structsJob) { 1042 t.Fatalf("bad %#v", structsJob) 1043 } 1044 }