github.com/mattyr/nomad@v0.3.3-0.20160919021406-3485a065154a/api/jobs.go (about) 1 package api 2 3 import ( 4 "fmt" 5 "sort" 6 "time" 7 ) 8 9 const ( 10 // JobTypeService indicates a long-running processes 11 JobTypeService = "service" 12 13 // JobTypeBatch indicates a short-lived process 14 JobTypeBatch = "batch" 15 ) 16 17 const ( 18 // RegisterEnforceIndexErrPrefix is the prefix to use in errors caused by 19 // enforcing the job modify index during registers. 20 RegisterEnforceIndexErrPrefix = "Enforcing job modify index" 21 ) 22 23 // Jobs is used to access the job-specific endpoints. 24 type Jobs struct { 25 client *Client 26 } 27 28 // Jobs returns a handle on the jobs endpoints. 29 func (c *Client) Jobs() *Jobs { 30 return &Jobs{client: c} 31 } 32 33 // Register is used to register a new job. It returns the ID 34 // of the evaluation, along with any errors encountered. 35 func (j *Jobs) Register(job *Job, q *WriteOptions) (string, *WriteMeta, error) { 36 37 var resp registerJobResponse 38 39 req := &RegisterJobRequest{Job: job} 40 wm, err := j.client.write("/v1/jobs", req, &resp, q) 41 if err != nil { 42 return "", nil, err 43 } 44 return resp.EvalID, wm, nil 45 } 46 47 // EnforceRegister is used to register a job enforcing its job modify index. 48 func (j *Jobs) EnforceRegister(job *Job, modifyIndex uint64, q *WriteOptions) (string, *WriteMeta, error) { 49 50 var resp registerJobResponse 51 52 req := &RegisterJobRequest{ 53 Job: job, 54 EnforceIndex: true, 55 JobModifyIndex: modifyIndex, 56 } 57 wm, err := j.client.write("/v1/jobs", req, &resp, q) 58 if err != nil { 59 return "", nil, err 60 } 61 return resp.EvalID, wm, nil 62 } 63 64 // List is used to list all of the existing jobs. 65 func (j *Jobs) List(q *QueryOptions) ([]*JobListStub, *QueryMeta, error) { 66 var resp []*JobListStub 67 qm, err := j.client.query("/v1/jobs", &resp, q) 68 if err != nil { 69 return nil, qm, err 70 } 71 sort.Sort(JobIDSort(resp)) 72 return resp, qm, nil 73 } 74 75 // PrefixList is used to list all existing jobs that match the prefix. 76 func (j *Jobs) PrefixList(prefix string) ([]*JobListStub, *QueryMeta, error) { 77 return j.List(&QueryOptions{Prefix: prefix}) 78 } 79 80 // Info is used to retrieve information about a particular 81 // job given its unique ID. 82 func (j *Jobs) Info(jobID string, q *QueryOptions) (*Job, *QueryMeta, error) { 83 var resp Job 84 qm, err := j.client.query("/v1/job/"+jobID, &resp, q) 85 if err != nil { 86 return nil, nil, err 87 } 88 return &resp, qm, nil 89 } 90 91 // Allocations is used to return the allocs for a given job ID. 92 func (j *Jobs) Allocations(jobID string, q *QueryOptions) ([]*AllocationListStub, *QueryMeta, error) { 93 var resp []*AllocationListStub 94 qm, err := j.client.query("/v1/job/"+jobID+"/allocations", &resp, q) 95 if err != nil { 96 return nil, nil, err 97 } 98 sort.Sort(AllocIndexSort(resp)) 99 return resp, qm, nil 100 } 101 102 // Evaluations is used to query the evaluations associated with 103 // the given job ID. 104 func (j *Jobs) Evaluations(jobID string, q *QueryOptions) ([]*Evaluation, *QueryMeta, error) { 105 var resp []*Evaluation 106 qm, err := j.client.query("/v1/job/"+jobID+"/evaluations", &resp, q) 107 if err != nil { 108 return nil, nil, err 109 } 110 sort.Sort(EvalIndexSort(resp)) 111 return resp, qm, nil 112 } 113 114 // Deregister is used to remove an existing job. 115 func (j *Jobs) Deregister(jobID string, q *WriteOptions) (string, *WriteMeta, error) { 116 var resp deregisterJobResponse 117 wm, err := j.client.delete("/v1/job/"+jobID, &resp, q) 118 if err != nil { 119 return "", nil, err 120 } 121 return resp.EvalID, wm, nil 122 } 123 124 // ForceEvaluate is used to force-evaluate an existing job. 125 func (j *Jobs) ForceEvaluate(jobID string, q *WriteOptions) (string, *WriteMeta, error) { 126 var resp registerJobResponse 127 wm, err := j.client.write("/v1/job/"+jobID+"/evaluate", nil, &resp, q) 128 if err != nil { 129 return "", nil, err 130 } 131 return resp.EvalID, wm, nil 132 } 133 134 // PeriodicForce spawns a new instance of the periodic job and returns the eval ID 135 func (j *Jobs) PeriodicForce(jobID string, q *WriteOptions) (string, *WriteMeta, error) { 136 var resp periodicForceResponse 137 wm, err := j.client.write("/v1/job/"+jobID+"/periodic/force", nil, &resp, q) 138 if err != nil { 139 return "", nil, err 140 } 141 return resp.EvalID, wm, nil 142 } 143 144 func (j *Jobs) Plan(job *Job, diff bool, q *WriteOptions) (*JobPlanResponse, *WriteMeta, error) { 145 if job == nil { 146 return nil, nil, fmt.Errorf("must pass non-nil job") 147 } 148 149 var resp JobPlanResponse 150 req := &JobPlanRequest{ 151 Job: job, 152 Diff: diff, 153 } 154 wm, err := j.client.write("/v1/job/"+job.ID+"/plan", req, &resp, q) 155 if err != nil { 156 return nil, nil, err 157 } 158 159 return &resp, wm, nil 160 } 161 162 func (j *Jobs) Summary(jobID string, q *QueryOptions) (*JobSummary, *QueryMeta, error) { 163 var resp JobSummary 164 qm, err := j.client.query("/v1/job/"+jobID+"/summary", &resp, q) 165 if err != nil { 166 return nil, nil, err 167 } 168 return &resp, qm, nil 169 } 170 171 // periodicForceResponse is used to deserialize a force response 172 type periodicForceResponse struct { 173 EvalID string 174 } 175 176 // UpdateStrategy is for serializing update strategy for a job. 177 type UpdateStrategy struct { 178 Stagger time.Duration 179 MaxParallel int 180 } 181 182 // PeriodicConfig is for serializing periodic config for a job. 183 type PeriodicConfig struct { 184 Enabled bool 185 Spec string 186 SpecType string 187 ProhibitOverlap bool 188 } 189 190 // Job is used to serialize a job. 191 type Job struct { 192 Region string 193 ID string 194 ParentID string 195 Name string 196 Type string 197 Priority int 198 AllAtOnce bool 199 Datacenters []string 200 Constraints []*Constraint 201 TaskGroups []*TaskGroup 202 Update *UpdateStrategy 203 Periodic *PeriodicConfig 204 Meta map[string]string 205 VaultToken string 206 Status string 207 StatusDescription string 208 CreateIndex uint64 209 ModifyIndex uint64 210 JobModifyIndex uint64 211 } 212 213 // JobSummary summarizes the state of the allocations of a job 214 type JobSummary struct { 215 JobID string 216 Summary map[string]TaskGroupSummary 217 218 // Raft Indexes 219 CreateIndex uint64 220 ModifyIndex uint64 221 } 222 223 // TaskGroup summarizes the state of all the allocations of a particular 224 // TaskGroup 225 type TaskGroupSummary struct { 226 Queued int 227 Complete int 228 Failed int 229 Running int 230 Starting int 231 Lost int 232 } 233 234 // JobListStub is used to return a subset of information about 235 // jobs during list operations. 236 type JobListStub struct { 237 ID string 238 ParentID string 239 Name string 240 Type string 241 Priority int 242 Status string 243 StatusDescription string 244 JobSummary *JobSummary 245 CreateIndex uint64 246 ModifyIndex uint64 247 JobModifyIndex uint64 248 } 249 250 // JobIDSort is used to sort jobs by their job ID's. 251 type JobIDSort []*JobListStub 252 253 func (j JobIDSort) Len() int { 254 return len(j) 255 } 256 257 func (j JobIDSort) Less(a, b int) bool { 258 return j[a].ID < j[b].ID 259 } 260 261 func (j JobIDSort) Swap(a, b int) { 262 j[a], j[b] = j[b], j[a] 263 } 264 265 // NewServiceJob creates and returns a new service-style job 266 // for long-lived processes using the provided name, ID, and 267 // relative job priority. 268 func NewServiceJob(id, name, region string, pri int) *Job { 269 return newJob(id, name, region, JobTypeService, pri) 270 } 271 272 // NewBatchJob creates and returns a new batch-style job for 273 // short-lived processes using the provided name and ID along 274 // with the relative job priority. 275 func NewBatchJob(id, name, region string, pri int) *Job { 276 return newJob(id, name, region, JobTypeBatch, pri) 277 } 278 279 // newJob is used to create a new Job struct. 280 func newJob(id, name, region, typ string, pri int) *Job { 281 return &Job{ 282 Region: region, 283 ID: id, 284 Name: name, 285 Type: typ, 286 Priority: pri, 287 } 288 } 289 290 // SetMeta is used to set arbitrary k/v pairs of metadata on a job. 291 func (j *Job) SetMeta(key, val string) *Job { 292 if j.Meta == nil { 293 j.Meta = make(map[string]string) 294 } 295 j.Meta[key] = val 296 return j 297 } 298 299 // AddDatacenter is used to add a datacenter to a job. 300 func (j *Job) AddDatacenter(dc string) *Job { 301 j.Datacenters = append(j.Datacenters, dc) 302 return j 303 } 304 305 // Constrain is used to add a constraint to a job. 306 func (j *Job) Constrain(c *Constraint) *Job { 307 j.Constraints = append(j.Constraints, c) 308 return j 309 } 310 311 // AddTaskGroup adds a task group to an existing job. 312 func (j *Job) AddTaskGroup(grp *TaskGroup) *Job { 313 j.TaskGroups = append(j.TaskGroups, grp) 314 return j 315 } 316 317 // AddPeriodicConfig adds a periodic config to an existing job. 318 func (j *Job) AddPeriodicConfig(cfg *PeriodicConfig) *Job { 319 j.Periodic = cfg 320 return j 321 } 322 323 // RegisterJobRequest is used to serialize a job registration 324 type RegisterJobRequest struct { 325 Job *Job 326 EnforceIndex bool `json:",omitempty"` 327 JobModifyIndex uint64 `json:",omitempty"` 328 } 329 330 // registerJobResponse is used to deserialize a job response 331 type registerJobResponse struct { 332 EvalID string 333 } 334 335 // deregisterJobResponse is used to decode a deregister response 336 type deregisterJobResponse struct { 337 EvalID string 338 } 339 340 type JobPlanRequest struct { 341 Job *Job 342 Diff bool 343 } 344 345 type JobPlanResponse struct { 346 JobModifyIndex uint64 347 CreatedEvals []*Evaluation 348 Diff *JobDiff 349 Annotations *PlanAnnotations 350 FailedTGAllocs map[string]*AllocationMetric 351 NextPeriodicLaunch time.Time 352 } 353 354 type JobDiff struct { 355 Type string 356 ID string 357 Fields []*FieldDiff 358 Objects []*ObjectDiff 359 TaskGroups []*TaskGroupDiff 360 } 361 362 type TaskGroupDiff struct { 363 Type string 364 Name string 365 Fields []*FieldDiff 366 Objects []*ObjectDiff 367 Tasks []*TaskDiff 368 Updates map[string]uint64 369 } 370 371 type TaskDiff struct { 372 Type string 373 Name string 374 Fields []*FieldDiff 375 Objects []*ObjectDiff 376 Annotations []string 377 } 378 379 type FieldDiff struct { 380 Type string 381 Name string 382 Old, New string 383 Annotations []string 384 } 385 386 type ObjectDiff struct { 387 Type string 388 Name string 389 Fields []*FieldDiff 390 Objects []*ObjectDiff 391 } 392 393 type PlanAnnotations struct { 394 DesiredTGUpdates map[string]*DesiredUpdates 395 } 396 397 type DesiredUpdates struct { 398 Ignore uint64 399 Place uint64 400 Migrate uint64 401 Stop uint64 402 InPlaceUpdate uint64 403 DestructiveUpdate uint64 404 }