github.com/emate/nomad@v0.8.2-wo-binpacking/api/jobs.go (about) 1 package api 2 3 import ( 4 "fmt" 5 "net/url" 6 "sort" 7 "strconv" 8 "time" 9 10 "github.com/gorhill/cronexpr" 11 "github.com/hashicorp/nomad/helper" 12 "github.com/hashicorp/nomad/nomad/structs" 13 ) 14 15 const ( 16 // JobTypeService indicates a long-running processes 17 JobTypeService = "service" 18 19 // JobTypeBatch indicates a short-lived process 20 JobTypeBatch = "batch" 21 22 // PeriodicSpecCron is used for a cron spec. 23 PeriodicSpecCron = "cron" 24 25 // DefaultNamespace is the default namespace. 26 DefaultNamespace = "default" 27 ) 28 29 const ( 30 // RegisterEnforceIndexErrPrefix is the prefix to use in errors caused by 31 // enforcing the job modify index during registers. 32 RegisterEnforceIndexErrPrefix = "Enforcing job modify index" 33 ) 34 35 // Jobs is used to access the job-specific endpoints. 36 type Jobs struct { 37 client *Client 38 } 39 40 // JobsParseRequest is used for arguments of the /vi/jobs/parse endpoint 41 type JobsParseRequest struct { 42 // JobHCL is an hcl jobspec 43 JobHCL string 44 45 // Canonicalize is a flag as to if the server should return default values 46 // for unset fields 47 Canonicalize bool 48 } 49 50 // Jobs returns a handle on the jobs endpoints. 51 func (c *Client) Jobs() *Jobs { 52 return &Jobs{client: c} 53 } 54 55 // Parse is used to convert the HCL repesentation of a Job to JSON server side. 56 // To parse the HCL client side see package github.com/hashicorp/nomad/jobspec 57 func (j *Jobs) ParseHCL(jobHCL string, canonicalize bool) (*Job, error) { 58 var job Job 59 req := &JobsParseRequest{ 60 JobHCL: jobHCL, 61 Canonicalize: canonicalize, 62 } 63 _, err := j.client.write("/v1/jobs/parse", req, &job, nil) 64 return &job, err 65 } 66 67 func (j *Jobs) Validate(job *Job, q *WriteOptions) (*JobValidateResponse, *WriteMeta, error) { 68 var resp JobValidateResponse 69 req := &JobValidateRequest{Job: job} 70 if q != nil { 71 req.WriteRequest = WriteRequest{Region: q.Region} 72 } 73 wm, err := j.client.write("/v1/validate/job", req, &resp, q) 74 return &resp, wm, err 75 } 76 77 // RegisterOptions is used to pass through job registration parameters 78 type RegisterOptions struct { 79 EnforceIndex bool 80 ModifyIndex uint64 81 PolicyOverride bool 82 } 83 84 // Register is used to register a new job. It returns the ID 85 // of the evaluation, along with any errors encountered. 86 func (j *Jobs) Register(job *Job, q *WriteOptions) (*JobRegisterResponse, *WriteMeta, error) { 87 return j.RegisterOpts(job, nil, q) 88 } 89 90 // EnforceRegister is used to register a job enforcing its job modify index. 91 func (j *Jobs) EnforceRegister(job *Job, modifyIndex uint64, q *WriteOptions) (*JobRegisterResponse, *WriteMeta, error) { 92 opts := RegisterOptions{EnforceIndex: true, ModifyIndex: modifyIndex} 93 return j.RegisterOpts(job, &opts, q) 94 } 95 96 // Register is used to register a new job. It returns the ID 97 // of the evaluation, along with any errors encountered. 98 func (j *Jobs) RegisterOpts(job *Job, opts *RegisterOptions, q *WriteOptions) (*JobRegisterResponse, *WriteMeta, error) { 99 // Format the request 100 req := &RegisterJobRequest{ 101 Job: job, 102 } 103 if opts != nil { 104 if opts.EnforceIndex { 105 req.EnforceIndex = true 106 req.JobModifyIndex = opts.ModifyIndex 107 } 108 if opts.PolicyOverride { 109 req.PolicyOverride = true 110 } 111 } 112 113 var resp JobRegisterResponse 114 wm, err := j.client.write("/v1/jobs", req, &resp, q) 115 if err != nil { 116 return nil, nil, err 117 } 118 return &resp, wm, nil 119 } 120 121 // List is used to list all of the existing jobs. 122 func (j *Jobs) List(q *QueryOptions) ([]*JobListStub, *QueryMeta, error) { 123 var resp []*JobListStub 124 qm, err := j.client.query("/v1/jobs", &resp, q) 125 if err != nil { 126 return nil, qm, err 127 } 128 sort.Sort(JobIDSort(resp)) 129 return resp, qm, nil 130 } 131 132 // PrefixList is used to list all existing jobs that match the prefix. 133 func (j *Jobs) PrefixList(prefix string) ([]*JobListStub, *QueryMeta, error) { 134 return j.List(&QueryOptions{Prefix: prefix}) 135 } 136 137 // Info is used to retrieve information about a particular 138 // job given its unique ID. 139 func (j *Jobs) Info(jobID string, q *QueryOptions) (*Job, *QueryMeta, error) { 140 var resp Job 141 qm, err := j.client.query("/v1/job/"+jobID, &resp, q) 142 if err != nil { 143 return nil, nil, err 144 } 145 return &resp, qm, nil 146 } 147 148 // Versions is used to retrieve all versions of a particular job given its 149 // unique ID. 150 func (j *Jobs) Versions(jobID string, diffs bool, q *QueryOptions) ([]*Job, []*JobDiff, *QueryMeta, error) { 151 var resp JobVersionsResponse 152 qm, err := j.client.query(fmt.Sprintf("/v1/job/%s/versions?diffs=%v", jobID, diffs), &resp, q) 153 if err != nil { 154 return nil, nil, nil, err 155 } 156 return resp.Versions, resp.Diffs, qm, nil 157 } 158 159 // Allocations is used to return the allocs for a given job ID. 160 func (j *Jobs) Allocations(jobID string, allAllocs bool, q *QueryOptions) ([]*AllocationListStub, *QueryMeta, error) { 161 var resp []*AllocationListStub 162 u, err := url.Parse("/v1/job/" + jobID + "/allocations") 163 if err != nil { 164 return nil, nil, err 165 } 166 167 v := u.Query() 168 v.Add("all", strconv.FormatBool(allAllocs)) 169 u.RawQuery = v.Encode() 170 171 qm, err := j.client.query(u.String(), &resp, q) 172 if err != nil { 173 return nil, nil, err 174 } 175 sort.Sort(AllocIndexSort(resp)) 176 return resp, qm, nil 177 } 178 179 // Deployments is used to query the deployments associated with the given job 180 // ID. 181 func (j *Jobs) Deployments(jobID string, q *QueryOptions) ([]*Deployment, *QueryMeta, error) { 182 var resp []*Deployment 183 qm, err := j.client.query("/v1/job/"+jobID+"/deployments", &resp, q) 184 if err != nil { 185 return nil, nil, err 186 } 187 sort.Sort(DeploymentIndexSort(resp)) 188 return resp, qm, nil 189 } 190 191 // LatestDeployment is used to query for the latest deployment associated with 192 // the given job ID. 193 func (j *Jobs) LatestDeployment(jobID string, q *QueryOptions) (*Deployment, *QueryMeta, error) { 194 var resp *Deployment 195 qm, err := j.client.query("/v1/job/"+jobID+"/deployment", &resp, q) 196 if err != nil { 197 return nil, nil, err 198 } 199 return resp, qm, nil 200 } 201 202 // Evaluations is used to query the evaluations associated with the given job 203 // ID. 204 func (j *Jobs) Evaluations(jobID string, q *QueryOptions) ([]*Evaluation, *QueryMeta, error) { 205 var resp []*Evaluation 206 qm, err := j.client.query("/v1/job/"+jobID+"/evaluations", &resp, q) 207 if err != nil { 208 return nil, nil, err 209 } 210 sort.Sort(EvalIndexSort(resp)) 211 return resp, qm, nil 212 } 213 214 // Deregister is used to remove an existing job. If purge is set to true, the job 215 // is deregistered and purged from the system versus still being queryable and 216 // eventually GC'ed from the system. Most callers should not specify purge. 217 func (j *Jobs) Deregister(jobID string, purge bool, q *WriteOptions) (string, *WriteMeta, error) { 218 var resp JobDeregisterResponse 219 wm, err := j.client.delete(fmt.Sprintf("/v1/job/%v?purge=%t", jobID, purge), &resp, q) 220 if err != nil { 221 return "", nil, err 222 } 223 return resp.EvalID, wm, nil 224 } 225 226 // ForceEvaluate is used to force-evaluate an existing job. 227 func (j *Jobs) ForceEvaluate(jobID string, q *WriteOptions) (string, *WriteMeta, error) { 228 var resp JobRegisterResponse 229 wm, err := j.client.write("/v1/job/"+jobID+"/evaluate", nil, &resp, q) 230 if err != nil { 231 return "", nil, err 232 } 233 return resp.EvalID, wm, nil 234 } 235 236 // PeriodicForce spawns a new instance of the periodic job and returns the eval ID 237 func (j *Jobs) PeriodicForce(jobID string, q *WriteOptions) (string, *WriteMeta, error) { 238 var resp periodicForceResponse 239 wm, err := j.client.write("/v1/job/"+jobID+"/periodic/force", nil, &resp, q) 240 if err != nil { 241 return "", nil, err 242 } 243 return resp.EvalID, wm, nil 244 } 245 246 // PlanOptions is used to pass through job planning parameters 247 type PlanOptions struct { 248 Diff bool 249 PolicyOverride bool 250 } 251 252 func (j *Jobs) Plan(job *Job, diff bool, q *WriteOptions) (*JobPlanResponse, *WriteMeta, error) { 253 opts := PlanOptions{Diff: diff} 254 return j.PlanOpts(job, &opts, q) 255 } 256 257 func (j *Jobs) PlanOpts(job *Job, opts *PlanOptions, q *WriteOptions) (*JobPlanResponse, *WriteMeta, error) { 258 if job == nil { 259 return nil, nil, fmt.Errorf("must pass non-nil job") 260 } 261 262 // Setup the request 263 req := &JobPlanRequest{ 264 Job: job, 265 } 266 if opts != nil { 267 req.Diff = opts.Diff 268 req.PolicyOverride = opts.PolicyOverride 269 } 270 271 var resp JobPlanResponse 272 wm, err := j.client.write("/v1/job/"+*job.ID+"/plan", req, &resp, q) 273 if err != nil { 274 return nil, nil, err 275 } 276 return &resp, wm, nil 277 } 278 279 func (j *Jobs) Summary(jobID string, q *QueryOptions) (*JobSummary, *QueryMeta, error) { 280 var resp JobSummary 281 qm, err := j.client.query("/v1/job/"+jobID+"/summary", &resp, q) 282 if err != nil { 283 return nil, nil, err 284 } 285 return &resp, qm, nil 286 } 287 288 func (j *Jobs) Dispatch(jobID string, meta map[string]string, 289 payload []byte, q *WriteOptions) (*JobDispatchResponse, *WriteMeta, error) { 290 var resp JobDispatchResponse 291 req := &JobDispatchRequest{ 292 JobID: jobID, 293 Meta: meta, 294 Payload: payload, 295 } 296 wm, err := j.client.write("/v1/job/"+jobID+"/dispatch", req, &resp, q) 297 if err != nil { 298 return nil, nil, err 299 } 300 return &resp, wm, nil 301 } 302 303 // Revert is used to revert the given job to the passed version. If 304 // enforceVersion is set, the job is only reverted if the current version is at 305 // the passed version. 306 func (j *Jobs) Revert(jobID string, version uint64, enforcePriorVersion *uint64, 307 q *WriteOptions) (*JobRegisterResponse, *WriteMeta, error) { 308 309 var resp JobRegisterResponse 310 req := &JobRevertRequest{ 311 JobID: jobID, 312 JobVersion: version, 313 EnforcePriorVersion: enforcePriorVersion, 314 } 315 wm, err := j.client.write("/v1/job/"+jobID+"/revert", req, &resp, q) 316 if err != nil { 317 return nil, nil, err 318 } 319 return &resp, wm, nil 320 } 321 322 // Stable is used to mark a job version's stability. 323 func (j *Jobs) Stable(jobID string, version uint64, stable bool, 324 q *WriteOptions) (*JobStabilityResponse, *WriteMeta, error) { 325 326 var resp JobStabilityResponse 327 req := &JobStabilityRequest{ 328 JobID: jobID, 329 JobVersion: version, 330 Stable: stable, 331 } 332 wm, err := j.client.write("/v1/job/"+jobID+"/stable", req, &resp, q) 333 if err != nil { 334 return nil, nil, err 335 } 336 return &resp, wm, nil 337 } 338 339 // periodicForceResponse is used to deserialize a force response 340 type periodicForceResponse struct { 341 EvalID string 342 } 343 344 // UpdateStrategy defines a task groups update strategy. 345 type UpdateStrategy struct { 346 Stagger *time.Duration `mapstructure:"stagger"` 347 MaxParallel *int `mapstructure:"max_parallel"` 348 HealthCheck *string `mapstructure:"health_check"` 349 MinHealthyTime *time.Duration `mapstructure:"min_healthy_time"` 350 HealthyDeadline *time.Duration `mapstructure:"healthy_deadline"` 351 AutoRevert *bool `mapstructure:"auto_revert"` 352 Canary *int `mapstructure:"canary"` 353 } 354 355 // DefaultUpdateStrategy provides a baseline that can be used to upgrade 356 // jobs with the old policy or for populating field defaults. 357 func DefaultUpdateStrategy() *UpdateStrategy { 358 return &UpdateStrategy{ 359 Stagger: helper.TimeToPtr(30 * time.Second), 360 MaxParallel: helper.IntToPtr(1), 361 HealthCheck: helper.StringToPtr("checks"), 362 MinHealthyTime: helper.TimeToPtr(10 * time.Second), 363 HealthyDeadline: helper.TimeToPtr(5 * time.Minute), 364 AutoRevert: helper.BoolToPtr(false), 365 Canary: helper.IntToPtr(0), 366 } 367 } 368 369 func (u *UpdateStrategy) Copy() *UpdateStrategy { 370 if u == nil { 371 return nil 372 } 373 374 copy := new(UpdateStrategy) 375 376 if u.Stagger != nil { 377 copy.Stagger = helper.TimeToPtr(*u.Stagger) 378 } 379 380 if u.MaxParallel != nil { 381 copy.MaxParallel = helper.IntToPtr(*u.MaxParallel) 382 } 383 384 if u.HealthCheck != nil { 385 copy.HealthCheck = helper.StringToPtr(*u.HealthCheck) 386 } 387 388 if u.MinHealthyTime != nil { 389 copy.MinHealthyTime = helper.TimeToPtr(*u.MinHealthyTime) 390 } 391 392 if u.HealthyDeadline != nil { 393 copy.HealthyDeadline = helper.TimeToPtr(*u.HealthyDeadline) 394 } 395 396 if u.AutoRevert != nil { 397 copy.AutoRevert = helper.BoolToPtr(*u.AutoRevert) 398 } 399 400 if u.Canary != nil { 401 copy.Canary = helper.IntToPtr(*u.Canary) 402 } 403 404 return copy 405 } 406 407 func (u *UpdateStrategy) Merge(o *UpdateStrategy) { 408 if o == nil { 409 return 410 } 411 412 if o.Stagger != nil { 413 u.Stagger = helper.TimeToPtr(*o.Stagger) 414 } 415 416 if o.MaxParallel != nil { 417 u.MaxParallel = helper.IntToPtr(*o.MaxParallel) 418 } 419 420 if o.HealthCheck != nil { 421 u.HealthCheck = helper.StringToPtr(*o.HealthCheck) 422 } 423 424 if o.MinHealthyTime != nil { 425 u.MinHealthyTime = helper.TimeToPtr(*o.MinHealthyTime) 426 } 427 428 if o.HealthyDeadline != nil { 429 u.HealthyDeadline = helper.TimeToPtr(*o.HealthyDeadline) 430 } 431 432 if o.AutoRevert != nil { 433 u.AutoRevert = helper.BoolToPtr(*o.AutoRevert) 434 } 435 436 if o.Canary != nil { 437 u.Canary = helper.IntToPtr(*o.Canary) 438 } 439 } 440 441 func (u *UpdateStrategy) Canonicalize() { 442 d := DefaultUpdateStrategy() 443 444 if u.MaxParallel == nil { 445 u.MaxParallel = d.MaxParallel 446 } 447 448 if u.Stagger == nil { 449 u.Stagger = d.Stagger 450 } 451 452 if u.HealthCheck == nil { 453 u.HealthCheck = d.HealthCheck 454 } 455 456 if u.HealthyDeadline == nil { 457 u.HealthyDeadline = d.HealthyDeadline 458 } 459 460 if u.MinHealthyTime == nil { 461 u.MinHealthyTime = d.MinHealthyTime 462 } 463 464 if u.AutoRevert == nil { 465 u.AutoRevert = d.AutoRevert 466 } 467 468 if u.Canary == nil { 469 u.Canary = d.Canary 470 } 471 } 472 473 // Empty returns whether the UpdateStrategy is empty or has user defined values. 474 func (u *UpdateStrategy) Empty() bool { 475 if u == nil { 476 return true 477 } 478 479 if u.Stagger != nil && *u.Stagger != 0 { 480 return false 481 } 482 483 if u.MaxParallel != nil && *u.MaxParallel != 0 { 484 return false 485 } 486 487 if u.HealthCheck != nil && *u.HealthCheck != "" { 488 return false 489 } 490 491 if u.MinHealthyTime != nil && *u.MinHealthyTime != 0 { 492 return false 493 } 494 495 if u.HealthyDeadline != nil && *u.HealthyDeadline != 0 { 496 return false 497 } 498 499 if u.AutoRevert != nil && *u.AutoRevert { 500 return false 501 } 502 503 if u.Canary != nil && *u.Canary != 0 { 504 return false 505 } 506 507 return true 508 } 509 510 // PeriodicConfig is for serializing periodic config for a job. 511 type PeriodicConfig struct { 512 Enabled *bool 513 Spec *string 514 SpecType *string 515 ProhibitOverlap *bool `mapstructure:"prohibit_overlap"` 516 TimeZone *string `mapstructure:"time_zone"` 517 } 518 519 func (p *PeriodicConfig) Canonicalize() { 520 if p.Enabled == nil { 521 p.Enabled = helper.BoolToPtr(true) 522 } 523 if p.Spec == nil { 524 p.Spec = helper.StringToPtr("") 525 } 526 if p.SpecType == nil { 527 p.SpecType = helper.StringToPtr(PeriodicSpecCron) 528 } 529 if p.ProhibitOverlap == nil { 530 p.ProhibitOverlap = helper.BoolToPtr(false) 531 } 532 if p.TimeZone == nil || *p.TimeZone == "" { 533 p.TimeZone = helper.StringToPtr("UTC") 534 } 535 } 536 537 // Next returns the closest time instant matching the spec that is after the 538 // passed time. If no matching instance exists, the zero value of time.Time is 539 // returned. The `time.Location` of the returned value matches that of the 540 // passed time. 541 func (p *PeriodicConfig) Next(fromTime time.Time) (time.Time, error) { 542 if *p.SpecType == PeriodicSpecCron { 543 if e, err := cronexpr.Parse(*p.Spec); err == nil { 544 return structs.CronParseNext(e, fromTime, *p.Spec) 545 } 546 } 547 548 return time.Time{}, nil 549 } 550 551 func (p *PeriodicConfig) GetLocation() (*time.Location, error) { 552 if p.TimeZone == nil || *p.TimeZone == "" { 553 return time.UTC, nil 554 } 555 556 return time.LoadLocation(*p.TimeZone) 557 } 558 559 // ParameterizedJobConfig is used to configure the parameterized job. 560 type ParameterizedJobConfig struct { 561 Payload string 562 MetaRequired []string `mapstructure:"meta_required"` 563 MetaOptional []string `mapstructure:"meta_optional"` 564 } 565 566 // Job is used to serialize a job. 567 type Job struct { 568 Stop *bool 569 Region *string 570 Namespace *string 571 ID *string 572 ParentID *string 573 Name *string 574 Type *string 575 Priority *int 576 AllAtOnce *bool `mapstructure:"all_at_once"` 577 Datacenters []string 578 Constraints []*Constraint 579 TaskGroups []*TaskGroup 580 Update *UpdateStrategy 581 Periodic *PeriodicConfig 582 ParameterizedJob *ParameterizedJobConfig 583 Payload []byte 584 Reschedule *ReschedulePolicy 585 Migrate *MigrateStrategy 586 Meta map[string]string 587 VaultToken *string `mapstructure:"vault_token"` 588 Status *string 589 StatusDescription *string 590 Stable *bool 591 Version *uint64 592 SubmitTime *int64 593 CreateIndex *uint64 594 ModifyIndex *uint64 595 JobModifyIndex *uint64 596 } 597 598 // IsPeriodic returns whether a job is periodic. 599 func (j *Job) IsPeriodic() bool { 600 return j.Periodic != nil 601 } 602 603 // IsParameterized returns whether a job is parameterized job. 604 func (j *Job) IsParameterized() bool { 605 return j.ParameterizedJob != nil 606 } 607 608 func (j *Job) Canonicalize() { 609 if j.ID == nil { 610 j.ID = helper.StringToPtr("") 611 } 612 if j.Name == nil { 613 j.Name = helper.StringToPtr(*j.ID) 614 } 615 if j.ParentID == nil { 616 j.ParentID = helper.StringToPtr("") 617 } 618 if j.Namespace == nil { 619 j.Namespace = helper.StringToPtr(DefaultNamespace) 620 } 621 if j.Priority == nil { 622 j.Priority = helper.IntToPtr(50) 623 } 624 if j.Stop == nil { 625 j.Stop = helper.BoolToPtr(false) 626 } 627 if j.Region == nil { 628 j.Region = helper.StringToPtr("global") 629 } 630 if j.Namespace == nil { 631 j.Namespace = helper.StringToPtr("default") 632 } 633 if j.Type == nil { 634 j.Type = helper.StringToPtr("service") 635 } 636 if j.AllAtOnce == nil { 637 j.AllAtOnce = helper.BoolToPtr(false) 638 } 639 if j.VaultToken == nil { 640 j.VaultToken = helper.StringToPtr("") 641 } 642 if j.Status == nil { 643 j.Status = helper.StringToPtr("") 644 } 645 if j.StatusDescription == nil { 646 j.StatusDescription = helper.StringToPtr("") 647 } 648 if j.Stable == nil { 649 j.Stable = helper.BoolToPtr(false) 650 } 651 if j.Version == nil { 652 j.Version = helper.Uint64ToPtr(0) 653 } 654 if j.CreateIndex == nil { 655 j.CreateIndex = helper.Uint64ToPtr(0) 656 } 657 if j.ModifyIndex == nil { 658 j.ModifyIndex = helper.Uint64ToPtr(0) 659 } 660 if j.JobModifyIndex == nil { 661 j.JobModifyIndex = helper.Uint64ToPtr(0) 662 } 663 if j.Periodic != nil { 664 j.Periodic.Canonicalize() 665 } 666 if j.Update != nil { 667 j.Update.Canonicalize() 668 } 669 670 for _, tg := range j.TaskGroups { 671 tg.Canonicalize(j) 672 } 673 } 674 675 // LookupTaskGroup finds a task group by name 676 func (j *Job) LookupTaskGroup(name string) *TaskGroup { 677 for _, tg := range j.TaskGroups { 678 if *tg.Name == name { 679 return tg 680 } 681 } 682 return nil 683 } 684 685 // JobSummary summarizes the state of the allocations of a job 686 type JobSummary struct { 687 JobID string 688 Namespace string 689 Summary map[string]TaskGroupSummary 690 Children *JobChildrenSummary 691 692 // Raft Indexes 693 CreateIndex uint64 694 ModifyIndex uint64 695 } 696 697 // JobChildrenSummary contains the summary of children job status 698 type JobChildrenSummary struct { 699 Pending int64 700 Running int64 701 Dead int64 702 } 703 704 func (jc *JobChildrenSummary) Sum() int { 705 if jc == nil { 706 return 0 707 } 708 709 return int(jc.Pending + jc.Running + jc.Dead) 710 } 711 712 // TaskGroup summarizes the state of all the allocations of a particular 713 // TaskGroup 714 type TaskGroupSummary struct { 715 Queued int 716 Complete int 717 Failed int 718 Running int 719 Starting int 720 Lost int 721 } 722 723 // JobListStub is used to return a subset of information about 724 // jobs during list operations. 725 type JobListStub struct { 726 ID string 727 ParentID string 728 Name string 729 Type string 730 Priority int 731 Periodic bool 732 ParameterizedJob bool 733 Stop bool 734 Status string 735 StatusDescription string 736 JobSummary *JobSummary 737 CreateIndex uint64 738 ModifyIndex uint64 739 JobModifyIndex uint64 740 SubmitTime int64 741 } 742 743 // JobIDSort is used to sort jobs by their job ID's. 744 type JobIDSort []*JobListStub 745 746 func (j JobIDSort) Len() int { 747 return len(j) 748 } 749 750 func (j JobIDSort) Less(a, b int) bool { 751 return j[a].ID < j[b].ID 752 } 753 754 func (j JobIDSort) Swap(a, b int) { 755 j[a], j[b] = j[b], j[a] 756 } 757 758 // NewServiceJob creates and returns a new service-style job 759 // for long-lived processes using the provided name, ID, and 760 // relative job priority. 761 func NewServiceJob(id, name, region string, pri int) *Job { 762 return newJob(id, name, region, JobTypeService, pri) 763 } 764 765 // NewBatchJob creates and returns a new batch-style job for 766 // short-lived processes using the provided name and ID along 767 // with the relative job priority. 768 func NewBatchJob(id, name, region string, pri int) *Job { 769 return newJob(id, name, region, JobTypeBatch, pri) 770 } 771 772 // newJob is used to create a new Job struct. 773 func newJob(id, name, region, typ string, pri int) *Job { 774 return &Job{ 775 Region: ®ion, 776 ID: &id, 777 Name: &name, 778 Type: &typ, 779 Priority: &pri, 780 } 781 } 782 783 // SetMeta is used to set arbitrary k/v pairs of metadata on a job. 784 func (j *Job) SetMeta(key, val string) *Job { 785 if j.Meta == nil { 786 j.Meta = make(map[string]string) 787 } 788 j.Meta[key] = val 789 return j 790 } 791 792 // AddDatacenter is used to add a datacenter to a job. 793 func (j *Job) AddDatacenter(dc string) *Job { 794 j.Datacenters = append(j.Datacenters, dc) 795 return j 796 } 797 798 // Constrain is used to add a constraint to a job. 799 func (j *Job) Constrain(c *Constraint) *Job { 800 j.Constraints = append(j.Constraints, c) 801 return j 802 } 803 804 // AddTaskGroup adds a task group to an existing job. 805 func (j *Job) AddTaskGroup(grp *TaskGroup) *Job { 806 j.TaskGroups = append(j.TaskGroups, grp) 807 return j 808 } 809 810 // AddPeriodicConfig adds a periodic config to an existing job. 811 func (j *Job) AddPeriodicConfig(cfg *PeriodicConfig) *Job { 812 j.Periodic = cfg 813 return j 814 } 815 816 type WriteRequest struct { 817 // The target region for this write 818 Region string 819 820 // Namespace is the target namespace for this write 821 Namespace string 822 823 // SecretID is the secret ID of an ACL token 824 SecretID string 825 } 826 827 // JobValidateRequest is used to validate a job 828 type JobValidateRequest struct { 829 Job *Job 830 WriteRequest 831 } 832 833 // JobValidateResponse is the response from validate request 834 type JobValidateResponse struct { 835 // DriverConfigValidated indicates whether the agent validated the driver 836 // config 837 DriverConfigValidated bool 838 839 // ValidationErrors is a list of validation errors 840 ValidationErrors []string 841 842 // Error is a string version of any error that may have occurred 843 Error string 844 845 // Warnings contains any warnings about the given job. These may include 846 // deprecation warnings. 847 Warnings string 848 } 849 850 // JobRevertRequest is used to revert a job to a prior version. 851 type JobRevertRequest struct { 852 // JobID is the ID of the job being reverted 853 JobID string 854 855 // JobVersion the version to revert to. 856 JobVersion uint64 857 858 // EnforcePriorVersion if set will enforce that the job is at the given 859 // version before reverting. 860 EnforcePriorVersion *uint64 861 862 WriteRequest 863 } 864 865 // JobUpdateRequest is used to update a job 866 type JobRegisterRequest struct { 867 Job *Job 868 // If EnforceIndex is set then the job will only be registered if the passed 869 // JobModifyIndex matches the current Jobs index. If the index is zero, the 870 // register only occurs if the job is new. 871 EnforceIndex bool 872 JobModifyIndex uint64 873 PolicyOverride bool 874 875 WriteRequest 876 } 877 878 // RegisterJobRequest is used to serialize a job registration 879 type RegisterJobRequest struct { 880 Job *Job 881 EnforceIndex bool `json:",omitempty"` 882 JobModifyIndex uint64 `json:",omitempty"` 883 PolicyOverride bool `json:",omitempty"` 884 } 885 886 // JobRegisterResponse is used to respond to a job registration 887 type JobRegisterResponse struct { 888 EvalID string 889 EvalCreateIndex uint64 890 JobModifyIndex uint64 891 892 // Warnings contains any warnings about the given job. These may include 893 // deprecation warnings. 894 Warnings string 895 896 QueryMeta 897 } 898 899 // JobDeregisterResponse is used to respond to a job deregistration 900 type JobDeregisterResponse struct { 901 EvalID string 902 EvalCreateIndex uint64 903 JobModifyIndex uint64 904 QueryMeta 905 } 906 907 type JobPlanRequest struct { 908 Job *Job 909 Diff bool 910 PolicyOverride bool 911 WriteRequest 912 } 913 914 type JobPlanResponse struct { 915 JobModifyIndex uint64 916 CreatedEvals []*Evaluation 917 Diff *JobDiff 918 Annotations *PlanAnnotations 919 FailedTGAllocs map[string]*AllocationMetric 920 NextPeriodicLaunch time.Time 921 922 // Warnings contains any warnings about the given job. These may include 923 // deprecation warnings. 924 Warnings string 925 } 926 927 type JobDiff struct { 928 Type string 929 ID string 930 Fields []*FieldDiff 931 Objects []*ObjectDiff 932 TaskGroups []*TaskGroupDiff 933 } 934 935 type TaskGroupDiff struct { 936 Type string 937 Name string 938 Fields []*FieldDiff 939 Objects []*ObjectDiff 940 Tasks []*TaskDiff 941 Updates map[string]uint64 942 } 943 944 type TaskDiff struct { 945 Type string 946 Name string 947 Fields []*FieldDiff 948 Objects []*ObjectDiff 949 Annotations []string 950 } 951 952 type FieldDiff struct { 953 Type string 954 Name string 955 Old, New string 956 Annotations []string 957 } 958 959 type ObjectDiff struct { 960 Type string 961 Name string 962 Fields []*FieldDiff 963 Objects []*ObjectDiff 964 } 965 966 type PlanAnnotations struct { 967 DesiredTGUpdates map[string]*DesiredUpdates 968 } 969 970 type DesiredUpdates struct { 971 Ignore uint64 972 Place uint64 973 Migrate uint64 974 Stop uint64 975 InPlaceUpdate uint64 976 DestructiveUpdate uint64 977 Canary uint64 978 } 979 980 type JobDispatchRequest struct { 981 JobID string 982 Payload []byte 983 Meta map[string]string 984 } 985 986 type JobDispatchResponse struct { 987 DispatchedJobID string 988 EvalID string 989 EvalCreateIndex uint64 990 JobCreateIndex uint64 991 WriteMeta 992 } 993 994 // JobVersionsResponse is used for a job get versions request 995 type JobVersionsResponse struct { 996 Versions []*Job 997 Diffs []*JobDiff 998 QueryMeta 999 } 1000 1001 // JobStabilityRequest is used to marked a job as stable. 1002 type JobStabilityRequest struct { 1003 // Job to set the stability on 1004 JobID string 1005 JobVersion uint64 1006 1007 // Set the stability 1008 Stable bool 1009 WriteRequest 1010 } 1011 1012 // JobStabilityResponse is the response when marking a job as stable. 1013 type JobStabilityResponse struct { 1014 JobModifyIndex uint64 1015 WriteMeta 1016 }