github.com/djenriquez/nomad-1@v0.8.1/command/agent/job_endpoint.go (about) 1 package agent 2 3 import ( 4 "fmt" 5 "net/http" 6 "strconv" 7 "strings" 8 9 "github.com/golang/snappy" 10 "github.com/hashicorp/nomad/api" 11 "github.com/hashicorp/nomad/nomad/structs" 12 ) 13 14 func (s *HTTPServer) JobsRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 15 switch req.Method { 16 case "GET": 17 return s.jobListRequest(resp, req) 18 case "PUT", "POST": 19 return s.jobUpdate(resp, req, "") 20 default: 21 return nil, CodedError(405, ErrInvalidMethod) 22 } 23 } 24 25 func (s *HTTPServer) jobListRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 26 args := structs.JobListRequest{} 27 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 28 return nil, nil 29 } 30 31 var out structs.JobListResponse 32 if err := s.agent.RPC("Job.List", &args, &out); err != nil { 33 return nil, err 34 } 35 36 setMeta(resp, &out.QueryMeta) 37 if out.Jobs == nil { 38 out.Jobs = make([]*structs.JobListStub, 0) 39 } 40 return out.Jobs, nil 41 } 42 43 func (s *HTTPServer) JobSpecificRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 44 path := strings.TrimPrefix(req.URL.Path, "/v1/job/") 45 switch { 46 case strings.HasSuffix(path, "/evaluate"): 47 jobName := strings.TrimSuffix(path, "/evaluate") 48 return s.jobForceEvaluate(resp, req, jobName) 49 case strings.HasSuffix(path, "/allocations"): 50 jobName := strings.TrimSuffix(path, "/allocations") 51 return s.jobAllocations(resp, req, jobName) 52 case strings.HasSuffix(path, "/evaluations"): 53 jobName := strings.TrimSuffix(path, "/evaluations") 54 return s.jobEvaluations(resp, req, jobName) 55 case strings.HasSuffix(path, "/periodic/force"): 56 jobName := strings.TrimSuffix(path, "/periodic/force") 57 return s.periodicForceRequest(resp, req, jobName) 58 case strings.HasSuffix(path, "/plan"): 59 jobName := strings.TrimSuffix(path, "/plan") 60 return s.jobPlan(resp, req, jobName) 61 case strings.HasSuffix(path, "/summary"): 62 jobName := strings.TrimSuffix(path, "/summary") 63 return s.jobSummaryRequest(resp, req, jobName) 64 case strings.HasSuffix(path, "/dispatch"): 65 jobName := strings.TrimSuffix(path, "/dispatch") 66 return s.jobDispatchRequest(resp, req, jobName) 67 case strings.HasSuffix(path, "/versions"): 68 jobName := strings.TrimSuffix(path, "/versions") 69 return s.jobVersions(resp, req, jobName) 70 case strings.HasSuffix(path, "/revert"): 71 jobName := strings.TrimSuffix(path, "/revert") 72 return s.jobRevert(resp, req, jobName) 73 case strings.HasSuffix(path, "/deployments"): 74 jobName := strings.TrimSuffix(path, "/deployments") 75 return s.jobDeployments(resp, req, jobName) 76 case strings.HasSuffix(path, "/deployment"): 77 jobName := strings.TrimSuffix(path, "/deployment") 78 return s.jobLatestDeployment(resp, req, jobName) 79 case strings.HasSuffix(path, "/stable"): 80 jobName := strings.TrimSuffix(path, "/stable") 81 return s.jobStable(resp, req, jobName) 82 default: 83 return s.jobCRUD(resp, req, path) 84 } 85 } 86 87 func (s *HTTPServer) jobForceEvaluate(resp http.ResponseWriter, req *http.Request, 88 jobName string) (interface{}, error) { 89 if req.Method != "PUT" && req.Method != "POST" { 90 return nil, CodedError(405, ErrInvalidMethod) 91 } 92 args := structs.JobEvaluateRequest{ 93 JobID: jobName, 94 } 95 s.parseWriteRequest(req, &args.WriteRequest) 96 97 var out structs.JobRegisterResponse 98 if err := s.agent.RPC("Job.Evaluate", &args, &out); err != nil { 99 return nil, err 100 } 101 setIndex(resp, out.Index) 102 return out, nil 103 } 104 105 func (s *HTTPServer) jobPlan(resp http.ResponseWriter, req *http.Request, 106 jobName string) (interface{}, error) { 107 if req.Method != "PUT" && req.Method != "POST" { 108 return nil, CodedError(405, ErrInvalidMethod) 109 } 110 111 var args api.JobPlanRequest 112 if err := decodeBody(req, &args); err != nil { 113 return nil, CodedError(400, err.Error()) 114 } 115 if args.Job == nil { 116 return nil, CodedError(400, "Job must be specified") 117 } 118 if args.Job.ID == nil { 119 return nil, CodedError(400, "Job must have a valid ID") 120 } 121 if jobName != "" && *args.Job.ID != jobName { 122 return nil, CodedError(400, "Job ID does not match") 123 } 124 125 sJob := ApiJobToStructJob(args.Job) 126 planReq := structs.JobPlanRequest{ 127 Job: sJob, 128 Diff: args.Diff, 129 PolicyOverride: args.PolicyOverride, 130 WriteRequest: structs.WriteRequest{ 131 Region: args.WriteRequest.Region, 132 }, 133 } 134 s.parseWriteRequest(req, &planReq.WriteRequest) 135 planReq.Namespace = sJob.Namespace 136 137 var out structs.JobPlanResponse 138 if err := s.agent.RPC("Job.Plan", &planReq, &out); err != nil { 139 return nil, err 140 } 141 setIndex(resp, out.Index) 142 return out, nil 143 } 144 145 func (s *HTTPServer) ValidateJobRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 146 // Ensure request method is POST or PUT 147 if !(req.Method == "POST" || req.Method == "PUT") { 148 return nil, CodedError(405, ErrInvalidMethod) 149 } 150 151 var validateRequest api.JobValidateRequest 152 if err := decodeBody(req, &validateRequest); err != nil { 153 return nil, CodedError(400, err.Error()) 154 } 155 if validateRequest.Job == nil { 156 return nil, CodedError(400, "Job must be specified") 157 } 158 159 job := ApiJobToStructJob(validateRequest.Job) 160 args := structs.JobValidateRequest{ 161 Job: job, 162 WriteRequest: structs.WriteRequest{ 163 Region: validateRequest.Region, 164 }, 165 } 166 s.parseWriteRequest(req, &args.WriteRequest) 167 args.Namespace = job.Namespace 168 169 var out structs.JobValidateResponse 170 if err := s.agent.RPC("Job.Validate", &args, &out); err != nil { 171 return nil, err 172 } 173 174 return out, nil 175 } 176 177 func (s *HTTPServer) periodicForceRequest(resp http.ResponseWriter, req *http.Request, 178 jobName string) (interface{}, error) { 179 if req.Method != "PUT" && req.Method != "POST" { 180 return nil, CodedError(405, ErrInvalidMethod) 181 } 182 183 args := structs.PeriodicForceRequest{ 184 JobID: jobName, 185 } 186 s.parseWriteRequest(req, &args.WriteRequest) 187 188 var out structs.PeriodicForceResponse 189 if err := s.agent.RPC("Periodic.Force", &args, &out); err != nil { 190 return nil, err 191 } 192 setIndex(resp, out.Index) 193 return out, nil 194 } 195 196 func (s *HTTPServer) jobAllocations(resp http.ResponseWriter, req *http.Request, 197 jobName string) (interface{}, error) { 198 if req.Method != "GET" { 199 return nil, CodedError(405, ErrInvalidMethod) 200 } 201 allAllocs, _ := strconv.ParseBool(req.URL.Query().Get("all")) 202 203 args := structs.JobSpecificRequest{ 204 JobID: jobName, 205 AllAllocs: allAllocs, 206 } 207 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 208 return nil, nil 209 } 210 211 var out structs.JobAllocationsResponse 212 if err := s.agent.RPC("Job.Allocations", &args, &out); err != nil { 213 return nil, err 214 } 215 216 setMeta(resp, &out.QueryMeta) 217 if out.Allocations == nil { 218 out.Allocations = make([]*structs.AllocListStub, 0) 219 } 220 for _, alloc := range out.Allocations { 221 alloc.SetEventDisplayMessages() 222 } 223 return out.Allocations, nil 224 } 225 226 func (s *HTTPServer) jobEvaluations(resp http.ResponseWriter, req *http.Request, 227 jobName string) (interface{}, error) { 228 if req.Method != "GET" { 229 return nil, CodedError(405, ErrInvalidMethod) 230 } 231 args := structs.JobSpecificRequest{ 232 JobID: jobName, 233 } 234 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 235 return nil, nil 236 } 237 238 var out structs.JobEvaluationsResponse 239 if err := s.agent.RPC("Job.Evaluations", &args, &out); err != nil { 240 return nil, err 241 } 242 243 setMeta(resp, &out.QueryMeta) 244 if out.Evaluations == nil { 245 out.Evaluations = make([]*structs.Evaluation, 0) 246 } 247 return out.Evaluations, nil 248 } 249 250 func (s *HTTPServer) jobDeployments(resp http.ResponseWriter, req *http.Request, 251 jobName string) (interface{}, error) { 252 if req.Method != "GET" { 253 return nil, CodedError(405, ErrInvalidMethod) 254 } 255 args := structs.JobSpecificRequest{ 256 JobID: jobName, 257 } 258 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 259 return nil, nil 260 } 261 262 var out structs.DeploymentListResponse 263 if err := s.agent.RPC("Job.Deployments", &args, &out); err != nil { 264 return nil, err 265 } 266 267 setMeta(resp, &out.QueryMeta) 268 if out.Deployments == nil { 269 out.Deployments = make([]*structs.Deployment, 0) 270 } 271 return out.Deployments, nil 272 } 273 274 func (s *HTTPServer) jobLatestDeployment(resp http.ResponseWriter, req *http.Request, 275 jobName string) (interface{}, error) { 276 if req.Method != "GET" { 277 return nil, CodedError(405, ErrInvalidMethod) 278 } 279 args := structs.JobSpecificRequest{ 280 JobID: jobName, 281 } 282 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 283 return nil, nil 284 } 285 286 var out structs.SingleDeploymentResponse 287 if err := s.agent.RPC("Job.LatestDeployment", &args, &out); err != nil { 288 return nil, err 289 } 290 291 setMeta(resp, &out.QueryMeta) 292 return out.Deployment, nil 293 } 294 295 func (s *HTTPServer) jobCRUD(resp http.ResponseWriter, req *http.Request, 296 jobName string) (interface{}, error) { 297 switch req.Method { 298 case "GET": 299 return s.jobQuery(resp, req, jobName) 300 case "PUT", "POST": 301 return s.jobUpdate(resp, req, jobName) 302 case "DELETE": 303 return s.jobDelete(resp, req, jobName) 304 default: 305 return nil, CodedError(405, ErrInvalidMethod) 306 } 307 } 308 309 func (s *HTTPServer) jobQuery(resp http.ResponseWriter, req *http.Request, 310 jobName string) (interface{}, error) { 311 args := structs.JobSpecificRequest{ 312 JobID: jobName, 313 } 314 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 315 return nil, nil 316 } 317 318 var out structs.SingleJobResponse 319 if err := s.agent.RPC("Job.GetJob", &args, &out); err != nil { 320 return nil, err 321 } 322 323 setMeta(resp, &out.QueryMeta) 324 if out.Job == nil { 325 return nil, CodedError(404, "job not found") 326 } 327 328 // Decode the payload if there is any 329 job := out.Job 330 if len(job.Payload) != 0 { 331 decoded, err := snappy.Decode(nil, out.Job.Payload) 332 if err != nil { 333 return nil, err 334 } 335 job = job.Copy() 336 job.Payload = decoded 337 } 338 339 return job, nil 340 } 341 342 func (s *HTTPServer) jobUpdate(resp http.ResponseWriter, req *http.Request, 343 jobName string) (interface{}, error) { 344 var args api.JobRegisterRequest 345 if err := decodeBody(req, &args); err != nil { 346 return nil, CodedError(400, err.Error()) 347 } 348 if args.Job == nil { 349 return nil, CodedError(400, "Job must be specified") 350 } 351 352 if args.Job.ID == nil { 353 return nil, CodedError(400, "Job ID hasn't been provided") 354 } 355 if jobName != "" && *args.Job.ID != jobName { 356 return nil, CodedError(400, "Job ID does not match name") 357 } 358 359 sJob := ApiJobToStructJob(args.Job) 360 361 regReq := structs.JobRegisterRequest{ 362 Job: sJob, 363 EnforceIndex: args.EnforceIndex, 364 JobModifyIndex: args.JobModifyIndex, 365 PolicyOverride: args.PolicyOverride, 366 WriteRequest: structs.WriteRequest{ 367 Region: args.WriteRequest.Region, 368 AuthToken: args.WriteRequest.SecretID, 369 }, 370 } 371 s.parseWriteRequest(req, ®Req.WriteRequest) 372 regReq.Namespace = sJob.Namespace 373 374 var out structs.JobRegisterResponse 375 if err := s.agent.RPC("Job.Register", ®Req, &out); err != nil { 376 return nil, err 377 } 378 setIndex(resp, out.Index) 379 return out, nil 380 } 381 382 func (s *HTTPServer) jobDelete(resp http.ResponseWriter, req *http.Request, 383 jobName string) (interface{}, error) { 384 385 purgeStr := req.URL.Query().Get("purge") 386 var purgeBool bool 387 if purgeStr != "" { 388 var err error 389 purgeBool, err = strconv.ParseBool(purgeStr) 390 if err != nil { 391 return nil, fmt.Errorf("Failed to parse value of %q (%v) as a bool: %v", "purge", purgeStr, err) 392 } 393 } 394 395 args := structs.JobDeregisterRequest{ 396 JobID: jobName, 397 Purge: purgeBool, 398 } 399 s.parseWriteRequest(req, &args.WriteRequest) 400 401 var out structs.JobDeregisterResponse 402 if err := s.agent.RPC("Job.Deregister", &args, &out); err != nil { 403 return nil, err 404 } 405 setIndex(resp, out.Index) 406 return out, nil 407 } 408 409 func (s *HTTPServer) jobVersions(resp http.ResponseWriter, req *http.Request, 410 jobName string) (interface{}, error) { 411 412 diffsStr := req.URL.Query().Get("diffs") 413 var diffsBool bool 414 if diffsStr != "" { 415 var err error 416 diffsBool, err = strconv.ParseBool(diffsStr) 417 if err != nil { 418 return nil, fmt.Errorf("Failed to parse value of %q (%v) as a bool: %v", "diffs", diffsStr, err) 419 } 420 } 421 422 args := structs.JobVersionsRequest{ 423 JobID: jobName, 424 Diffs: diffsBool, 425 } 426 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 427 return nil, nil 428 } 429 430 var out structs.JobVersionsResponse 431 if err := s.agent.RPC("Job.GetJobVersions", &args, &out); err != nil { 432 return nil, err 433 } 434 435 setMeta(resp, &out.QueryMeta) 436 if len(out.Versions) == 0 { 437 return nil, CodedError(404, "job versions not found") 438 } 439 440 return out, nil 441 } 442 443 func (s *HTTPServer) jobRevert(resp http.ResponseWriter, req *http.Request, 444 jobName string) (interface{}, error) { 445 446 if req.Method != "PUT" && req.Method != "POST" { 447 return nil, CodedError(405, ErrInvalidMethod) 448 } 449 450 var revertRequest structs.JobRevertRequest 451 if err := decodeBody(req, &revertRequest); err != nil { 452 return nil, CodedError(400, err.Error()) 453 } 454 if revertRequest.JobID == "" { 455 return nil, CodedError(400, "JobID must be specified") 456 } 457 if revertRequest.JobID != jobName { 458 return nil, CodedError(400, "Job ID does not match") 459 } 460 461 s.parseWriteRequest(req, &revertRequest.WriteRequest) 462 463 var out structs.JobRegisterResponse 464 if err := s.agent.RPC("Job.Revert", &revertRequest, &out); err != nil { 465 return nil, err 466 } 467 468 setMeta(resp, &out.QueryMeta) 469 return out, nil 470 } 471 472 func (s *HTTPServer) jobStable(resp http.ResponseWriter, req *http.Request, 473 jobName string) (interface{}, error) { 474 475 if req.Method != "PUT" && req.Method != "POST" { 476 return nil, CodedError(405, ErrInvalidMethod) 477 } 478 479 var stableRequest structs.JobStabilityRequest 480 if err := decodeBody(req, &stableRequest); err != nil { 481 return nil, CodedError(400, err.Error()) 482 } 483 if stableRequest.JobID == "" { 484 return nil, CodedError(400, "JobID must be specified") 485 } 486 if stableRequest.JobID != jobName { 487 return nil, CodedError(400, "Job ID does not match") 488 } 489 490 s.parseWriteRequest(req, &stableRequest.WriteRequest) 491 492 var out structs.JobStabilityResponse 493 if err := s.agent.RPC("Job.Stable", &stableRequest, &out); err != nil { 494 return nil, err 495 } 496 497 setIndex(resp, out.Index) 498 return out, nil 499 } 500 501 func (s *HTTPServer) jobSummaryRequest(resp http.ResponseWriter, req *http.Request, name string) (interface{}, error) { 502 args := structs.JobSummaryRequest{ 503 JobID: name, 504 } 505 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 506 return nil, nil 507 } 508 509 var out structs.JobSummaryResponse 510 if err := s.agent.RPC("Job.Summary", &args, &out); err != nil { 511 return nil, err 512 } 513 514 setMeta(resp, &out.QueryMeta) 515 if out.JobSummary == nil { 516 return nil, CodedError(404, "job not found") 517 } 518 setIndex(resp, out.Index) 519 return out.JobSummary, nil 520 } 521 522 func (s *HTTPServer) jobDispatchRequest(resp http.ResponseWriter, req *http.Request, name string) (interface{}, error) { 523 if req.Method != "PUT" && req.Method != "POST" { 524 return nil, CodedError(405, ErrInvalidMethod) 525 } 526 args := structs.JobDispatchRequest{} 527 if err := decodeBody(req, &args); err != nil { 528 return nil, CodedError(400, err.Error()) 529 } 530 if args.JobID != "" && args.JobID != name { 531 return nil, CodedError(400, "Job ID does not match") 532 } 533 if args.JobID == "" { 534 args.JobID = name 535 } 536 537 s.parseWriteRequest(req, &args.WriteRequest) 538 539 var out structs.JobDispatchResponse 540 if err := s.agent.RPC("Job.Dispatch", &args, &out); err != nil { 541 return nil, err 542 } 543 setIndex(resp, out.Index) 544 return out, nil 545 } 546 547 func ApiJobToStructJob(job *api.Job) *structs.Job { 548 job.Canonicalize() 549 550 j := &structs.Job{ 551 Stop: *job.Stop, 552 Region: *job.Region, 553 Namespace: *job.Namespace, 554 ID: *job.ID, 555 ParentID: *job.ParentID, 556 Name: *job.Name, 557 Type: *job.Type, 558 Priority: *job.Priority, 559 AllAtOnce: *job.AllAtOnce, 560 Datacenters: job.Datacenters, 561 Payload: job.Payload, 562 Meta: job.Meta, 563 VaultToken: *job.VaultToken, 564 } 565 566 if l := len(job.Constraints); l != 0 { 567 j.Constraints = make([]*structs.Constraint, l) 568 for i, c := range job.Constraints { 569 con := &structs.Constraint{} 570 ApiConstraintToStructs(c, con) 571 j.Constraints[i] = con 572 } 573 } 574 575 // COMPAT: Remove in 0.7.0. Update has been pushed into the task groups 576 if job.Update != nil { 577 j.Update = structs.UpdateStrategy{} 578 579 if job.Update.Stagger != nil { 580 j.Update.Stagger = *job.Update.Stagger 581 } 582 if job.Update.MaxParallel != nil { 583 j.Update.MaxParallel = *job.Update.MaxParallel 584 } 585 } 586 587 if job.Periodic != nil { 588 j.Periodic = &structs.PeriodicConfig{ 589 Enabled: *job.Periodic.Enabled, 590 SpecType: *job.Periodic.SpecType, 591 ProhibitOverlap: *job.Periodic.ProhibitOverlap, 592 TimeZone: *job.Periodic.TimeZone, 593 } 594 595 if job.Periodic.Spec != nil { 596 j.Periodic.Spec = *job.Periodic.Spec 597 } 598 } 599 600 if job.ParameterizedJob != nil { 601 j.ParameterizedJob = &structs.ParameterizedJobConfig{ 602 Payload: job.ParameterizedJob.Payload, 603 MetaRequired: job.ParameterizedJob.MetaRequired, 604 MetaOptional: job.ParameterizedJob.MetaOptional, 605 } 606 } 607 608 if l := len(job.TaskGroups); l != 0 { 609 j.TaskGroups = make([]*structs.TaskGroup, l) 610 for i, taskGroup := range job.TaskGroups { 611 tg := &structs.TaskGroup{} 612 ApiTgToStructsTG(taskGroup, tg) 613 j.TaskGroups[i] = tg 614 } 615 } 616 617 return j 618 } 619 620 func ApiTgToStructsTG(taskGroup *api.TaskGroup, tg *structs.TaskGroup) { 621 tg.Name = *taskGroup.Name 622 tg.Count = *taskGroup.Count 623 tg.Meta = taskGroup.Meta 624 625 if l := len(taskGroup.Constraints); l != 0 { 626 tg.Constraints = make([]*structs.Constraint, l) 627 for k, constraint := range taskGroup.Constraints { 628 c := &structs.Constraint{} 629 ApiConstraintToStructs(constraint, c) 630 tg.Constraints[k] = c 631 } 632 } 633 634 tg.RestartPolicy = &structs.RestartPolicy{ 635 Attempts: *taskGroup.RestartPolicy.Attempts, 636 Interval: *taskGroup.RestartPolicy.Interval, 637 Delay: *taskGroup.RestartPolicy.Delay, 638 Mode: *taskGroup.RestartPolicy.Mode, 639 } 640 641 if taskGroup.ReschedulePolicy != nil { 642 tg.ReschedulePolicy = &structs.ReschedulePolicy{ 643 Attempts: *taskGroup.ReschedulePolicy.Attempts, 644 Interval: *taskGroup.ReschedulePolicy.Interval, 645 Delay: *taskGroup.ReschedulePolicy.Delay, 646 DelayFunction: *taskGroup.ReschedulePolicy.DelayFunction, 647 MaxDelay: *taskGroup.ReschedulePolicy.MaxDelay, 648 Unlimited: *taskGroup.ReschedulePolicy.Unlimited, 649 } 650 } 651 652 if taskGroup.Migrate != nil { 653 tg.Migrate = &structs.MigrateStrategy{ 654 MaxParallel: *taskGroup.Migrate.MaxParallel, 655 HealthCheck: *taskGroup.Migrate.HealthCheck, 656 MinHealthyTime: *taskGroup.Migrate.MinHealthyTime, 657 HealthyDeadline: *taskGroup.Migrate.HealthyDeadline, 658 } 659 } 660 661 tg.EphemeralDisk = &structs.EphemeralDisk{ 662 Sticky: *taskGroup.EphemeralDisk.Sticky, 663 SizeMB: *taskGroup.EphemeralDisk.SizeMB, 664 Migrate: *taskGroup.EphemeralDisk.Migrate, 665 } 666 667 if taskGroup.Update != nil { 668 tg.Update = &structs.UpdateStrategy{ 669 Stagger: *taskGroup.Update.Stagger, 670 MaxParallel: *taskGroup.Update.MaxParallel, 671 HealthCheck: *taskGroup.Update.HealthCheck, 672 MinHealthyTime: *taskGroup.Update.MinHealthyTime, 673 HealthyDeadline: *taskGroup.Update.HealthyDeadline, 674 AutoRevert: *taskGroup.Update.AutoRevert, 675 Canary: *taskGroup.Update.Canary, 676 } 677 } 678 679 if l := len(taskGroup.Tasks); l != 0 { 680 tg.Tasks = make([]*structs.Task, l) 681 for l, task := range taskGroup.Tasks { 682 t := &structs.Task{} 683 ApiTaskToStructsTask(task, t) 684 tg.Tasks[l] = t 685 } 686 } 687 } 688 689 // ApiTaskToStructsTask is a copy and type conversion between the API 690 // representation of a task from a struct representation of a task. 691 func ApiTaskToStructsTask(apiTask *api.Task, structsTask *structs.Task) { 692 structsTask.Name = apiTask.Name 693 structsTask.Driver = apiTask.Driver 694 structsTask.User = apiTask.User 695 structsTask.Leader = apiTask.Leader 696 structsTask.Config = apiTask.Config 697 structsTask.Env = apiTask.Env 698 structsTask.Meta = apiTask.Meta 699 structsTask.KillTimeout = *apiTask.KillTimeout 700 structsTask.ShutdownDelay = apiTask.ShutdownDelay 701 structsTask.KillSignal = apiTask.KillSignal 702 703 if l := len(apiTask.Constraints); l != 0 { 704 structsTask.Constraints = make([]*structs.Constraint, l) 705 for i, constraint := range apiTask.Constraints { 706 c := &structs.Constraint{} 707 ApiConstraintToStructs(constraint, c) 708 structsTask.Constraints[i] = c 709 } 710 } 711 712 if l := len(apiTask.Services); l != 0 { 713 structsTask.Services = make([]*structs.Service, l) 714 for i, service := range apiTask.Services { 715 structsTask.Services[i] = &structs.Service{ 716 Name: service.Name, 717 PortLabel: service.PortLabel, 718 Tags: service.Tags, 719 AddressMode: service.AddressMode, 720 } 721 722 if l := len(service.Checks); l != 0 { 723 structsTask.Services[i].Checks = make([]*structs.ServiceCheck, l) 724 for j, check := range service.Checks { 725 structsTask.Services[i].Checks[j] = &structs.ServiceCheck{ 726 Name: check.Name, 727 Type: check.Type, 728 Command: check.Command, 729 Args: check.Args, 730 Path: check.Path, 731 Protocol: check.Protocol, 732 PortLabel: check.PortLabel, 733 AddressMode: check.AddressMode, 734 Interval: check.Interval, 735 Timeout: check.Timeout, 736 InitialStatus: check.InitialStatus, 737 TLSSkipVerify: check.TLSSkipVerify, 738 Header: check.Header, 739 Method: check.Method, 740 } 741 if check.CheckRestart != nil { 742 structsTask.Services[i].Checks[j].CheckRestart = &structs.CheckRestart{ 743 Limit: check.CheckRestart.Limit, 744 Grace: *check.CheckRestart.Grace, 745 IgnoreWarnings: check.CheckRestart.IgnoreWarnings, 746 } 747 } 748 } 749 } 750 } 751 } 752 753 structsTask.Resources = &structs.Resources{ 754 CPU: *apiTask.Resources.CPU, 755 MemoryMB: *apiTask.Resources.MemoryMB, 756 IOPS: *apiTask.Resources.IOPS, 757 } 758 759 if l := len(apiTask.Resources.Networks); l != 0 { 760 structsTask.Resources.Networks = make([]*structs.NetworkResource, l) 761 for i, nw := range apiTask.Resources.Networks { 762 structsTask.Resources.Networks[i] = &structs.NetworkResource{ 763 CIDR: nw.CIDR, 764 IP: nw.IP, 765 MBits: *nw.MBits, 766 } 767 768 if l := len(nw.DynamicPorts); l != 0 { 769 structsTask.Resources.Networks[i].DynamicPorts = make([]structs.Port, l) 770 for j, dp := range nw.DynamicPorts { 771 structsTask.Resources.Networks[i].DynamicPorts[j] = structs.Port{ 772 Label: dp.Label, 773 Value: dp.Value, 774 } 775 } 776 } 777 778 if l := len(nw.ReservedPorts); l != 0 { 779 structsTask.Resources.Networks[i].ReservedPorts = make([]structs.Port, l) 780 for j, rp := range nw.ReservedPorts { 781 structsTask.Resources.Networks[i].ReservedPorts[j] = structs.Port{ 782 Label: rp.Label, 783 Value: rp.Value, 784 } 785 } 786 } 787 } 788 } 789 790 structsTask.LogConfig = &structs.LogConfig{ 791 MaxFiles: *apiTask.LogConfig.MaxFiles, 792 MaxFileSizeMB: *apiTask.LogConfig.MaxFileSizeMB, 793 } 794 795 if l := len(apiTask.Artifacts); l != 0 { 796 structsTask.Artifacts = make([]*structs.TaskArtifact, l) 797 for k, ta := range apiTask.Artifacts { 798 structsTask.Artifacts[k] = &structs.TaskArtifact{ 799 GetterSource: *ta.GetterSource, 800 GetterOptions: ta.GetterOptions, 801 GetterMode: *ta.GetterMode, 802 RelativeDest: *ta.RelativeDest, 803 } 804 } 805 } 806 807 if apiTask.Vault != nil { 808 structsTask.Vault = &structs.Vault{ 809 Policies: apiTask.Vault.Policies, 810 Env: *apiTask.Vault.Env, 811 ChangeMode: *apiTask.Vault.ChangeMode, 812 ChangeSignal: *apiTask.Vault.ChangeSignal, 813 } 814 } 815 816 if l := len(apiTask.Templates); l != 0 { 817 structsTask.Templates = make([]*structs.Template, l) 818 for i, template := range apiTask.Templates { 819 structsTask.Templates[i] = &structs.Template{ 820 SourcePath: *template.SourcePath, 821 DestPath: *template.DestPath, 822 EmbeddedTmpl: *template.EmbeddedTmpl, 823 ChangeMode: *template.ChangeMode, 824 ChangeSignal: *template.ChangeSignal, 825 Splay: *template.Splay, 826 Perms: *template.Perms, 827 LeftDelim: *template.LeftDelim, 828 RightDelim: *template.RightDelim, 829 Envvars: *template.Envvars, 830 VaultGrace: *template.VaultGrace, 831 } 832 } 833 } 834 835 if apiTask.DispatchPayload != nil { 836 structsTask.DispatchPayload = &structs.DispatchPayloadConfig{ 837 File: apiTask.DispatchPayload.File, 838 } 839 } 840 } 841 842 func ApiConstraintToStructs(c1 *api.Constraint, c2 *structs.Constraint) { 843 c2.LTarget = c1.LTarget 844 c2.RTarget = c1.RTarget 845 c2.Operand = c1.Operand 846 }