github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/nomad/job_endpoint.go (about) 1 package nomad 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/armon/go-metrics" 8 "github.com/hashicorp/nomad/nomad/structs" 9 ) 10 11 // Job endpoint is used for job interactions 12 type Job struct { 13 srv *Server 14 } 15 16 // Register is used to upsert a job for scheduling 17 func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegisterResponse) error { 18 if done, err := j.srv.forward("Job.Register", args, args, reply); done { 19 return err 20 } 21 defer metrics.MeasureSince([]string{"nomad", "job", "register"}, time.Now()) 22 23 // Validate the arguments 24 if args.Job == nil { 25 return fmt.Errorf("missing job for registration") 26 } 27 if err := args.Job.Validate(); err != nil { 28 return err 29 } 30 if args.Job.Type == structs.JobTypeCore { 31 return fmt.Errorf("job type cannot be core") 32 } 33 34 // Commit this update via Raft 35 _, index, err := j.srv.raftApply(structs.JobRegisterRequestType, args) 36 if err != nil { 37 j.srv.logger.Printf("[ERR] nomad.job: Register failed: %v", err) 38 return err 39 } 40 41 // Create a new evaluation 42 eval := &structs.Evaluation{ 43 ID: structs.GenerateUUID(), 44 Priority: args.Job.Priority, 45 Type: args.Job.Type, 46 TriggeredBy: structs.EvalTriggerJobRegister, 47 JobID: args.Job.ID, 48 JobModifyIndex: index, 49 Status: structs.EvalStatusPending, 50 } 51 update := &structs.EvalUpdateRequest{ 52 Evals: []*structs.Evaluation{eval}, 53 WriteRequest: structs.WriteRequest{Region: args.Region}, 54 } 55 56 // Commit this evaluation via Raft 57 // XXX: There is a risk of partial failure where the JobRegister succeeds 58 // but that the EvalUpdate does not. 59 _, evalIndex, err := j.srv.raftApply(structs.EvalUpdateRequestType, update) 60 if err != nil { 61 j.srv.logger.Printf("[ERR] nomad.job: Eval create failed: %v", err) 62 return err 63 } 64 65 // Setup the reply 66 reply.EvalID = eval.ID 67 reply.EvalCreateIndex = evalIndex 68 reply.JobModifyIndex = index 69 reply.Index = evalIndex 70 return nil 71 } 72 73 // Evaluate is used to force a job for re-evaluation 74 func (j *Job) Evaluate(args *structs.JobEvaluateRequest, reply *structs.JobRegisterResponse) error { 75 if done, err := j.srv.forward("Job.Evaluate", args, args, reply); done { 76 return err 77 } 78 defer metrics.MeasureSince([]string{"nomad", "job", "evaluate"}, time.Now()) 79 80 // Validate the arguments 81 if args.JobID == "" { 82 return fmt.Errorf("missing job ID for evaluation") 83 } 84 85 // Lookup the job 86 snap, err := j.srv.fsm.State().Snapshot() 87 if err != nil { 88 return err 89 } 90 job, err := snap.JobByID(args.JobID) 91 if err != nil { 92 return err 93 } 94 if job == nil { 95 return fmt.Errorf("job not found") 96 } 97 98 // Create a new evaluation 99 eval := &structs.Evaluation{ 100 ID: structs.GenerateUUID(), 101 Priority: job.Priority, 102 Type: job.Type, 103 TriggeredBy: structs.EvalTriggerJobRegister, 104 JobID: job.ID, 105 JobModifyIndex: job.ModifyIndex, 106 Status: structs.EvalStatusPending, 107 } 108 update := &structs.EvalUpdateRequest{ 109 Evals: []*structs.Evaluation{eval}, 110 WriteRequest: structs.WriteRequest{Region: args.Region}, 111 } 112 113 // Commit this evaluation via Raft 114 _, evalIndex, err := j.srv.raftApply(structs.EvalUpdateRequestType, update) 115 if err != nil { 116 j.srv.logger.Printf("[ERR] nomad.job: Eval create failed: %v", err) 117 return err 118 } 119 120 // Setup the reply 121 reply.EvalID = eval.ID 122 reply.EvalCreateIndex = evalIndex 123 reply.JobModifyIndex = job.ModifyIndex 124 reply.Index = evalIndex 125 return nil 126 } 127 128 // Deregister is used to remove a job the cluster. 129 func (j *Job) Deregister(args *structs.JobDeregisterRequest, reply *structs.JobDeregisterResponse) error { 130 if done, err := j.srv.forward("Job.Deregister", args, args, reply); done { 131 return err 132 } 133 defer metrics.MeasureSince([]string{"nomad", "job", "deregister"}, time.Now()) 134 135 // Commit this update via Raft 136 _, index, err := j.srv.raftApply(structs.JobDeregisterRequestType, args) 137 if err != nil { 138 j.srv.logger.Printf("[ERR] nomad.job: Deregister failed: %v", err) 139 return err 140 } 141 142 // Create a new evaluation 143 // XXX: The job priority / type is strange for this, since it's not a high 144 // priority even if the job was. The scheduler itself also doesn't matter, 145 // since all should be able to handle deregistration in the same way. 146 eval := &structs.Evaluation{ 147 ID: structs.GenerateUUID(), 148 Priority: structs.JobDefaultPriority, 149 Type: structs.JobTypeService, 150 TriggeredBy: structs.EvalTriggerJobDeregister, 151 JobID: args.JobID, 152 JobModifyIndex: index, 153 Status: structs.EvalStatusPending, 154 } 155 update := &structs.EvalUpdateRequest{ 156 Evals: []*structs.Evaluation{eval}, 157 WriteRequest: structs.WriteRequest{Region: args.Region}, 158 } 159 160 // Commit this evaluation via Raft 161 _, evalIndex, err := j.srv.raftApply(structs.EvalUpdateRequestType, update) 162 if err != nil { 163 j.srv.logger.Printf("[ERR] nomad.job: Eval create failed: %v", err) 164 return err 165 } 166 167 // Setup the reply 168 reply.EvalID = eval.ID 169 reply.EvalCreateIndex = evalIndex 170 reply.JobModifyIndex = index 171 reply.Index = evalIndex 172 return nil 173 } 174 175 // GetJob is used to request information about a specific job 176 func (j *Job) GetJob(args *structs.JobSpecificRequest, 177 reply *structs.SingleJobResponse) error { 178 if done, err := j.srv.forward("Job.GetJob", args, args, reply); done { 179 return err 180 } 181 defer metrics.MeasureSince([]string{"nomad", "job", "get_job"}, time.Now()) 182 183 // Look for the job 184 snap, err := j.srv.fsm.State().Snapshot() 185 if err != nil { 186 return err 187 } 188 out, err := snap.JobByID(args.JobID) 189 if err != nil { 190 return err 191 } 192 193 // Setup the output 194 if out != nil { 195 reply.Job = out 196 reply.Index = out.ModifyIndex 197 } else { 198 // Use the last index that affected the nodes table 199 index, err := snap.Index("jobs") 200 if err != nil { 201 return err 202 } 203 reply.Index = index 204 } 205 206 // Set the query response 207 j.srv.setQueryMeta(&reply.QueryMeta) 208 return nil 209 } 210 211 // List is used to list the jobs registered in the system 212 func (j *Job) List(args *structs.JobListRequest, 213 reply *structs.JobListResponse) error { 214 if done, err := j.srv.forward("Job.List", args, args, reply); done { 215 return err 216 } 217 defer metrics.MeasureSince([]string{"nomad", "job", "list"}, time.Now()) 218 219 // Capture all the jobs 220 snap, err := j.srv.fsm.State().Snapshot() 221 if err != nil { 222 return err 223 } 224 iter, err := snap.Jobs() 225 if err != nil { 226 return err 227 } 228 229 for { 230 raw := iter.Next() 231 if raw == nil { 232 break 233 } 234 job := raw.(*structs.Job) 235 reply.Jobs = append(reply.Jobs, job.Stub()) 236 } 237 238 // Use the last index that affected the jobs table 239 index, err := snap.Index("jobs") 240 if err != nil { 241 return err 242 } 243 reply.Index = index 244 245 // Set the query response 246 j.srv.setQueryMeta(&reply.QueryMeta) 247 return nil 248 } 249 250 // Allocations is used to list the allocations for a job 251 func (j *Job) Allocations(args *structs.JobSpecificRequest, 252 reply *structs.JobAllocationsResponse) error { 253 if done, err := j.srv.forward("Job.Allocations", args, args, reply); done { 254 return err 255 } 256 defer metrics.MeasureSince([]string{"nomad", "job", "allocations"}, time.Now()) 257 258 // Capture the allocations 259 snap, err := j.srv.fsm.State().Snapshot() 260 if err != nil { 261 return err 262 } 263 allocs, err := snap.AllocsByJob(args.JobID) 264 if err != nil { 265 return err 266 } 267 268 // Convert to stubs 269 if len(allocs) > 0 { 270 reply.Allocations = make([]*structs.AllocListStub, 0, len(allocs)) 271 for _, alloc := range allocs { 272 reply.Allocations = append(reply.Allocations, alloc.Stub()) 273 } 274 } 275 276 // Use the last index that affected the allocs table 277 index, err := snap.Index("allocs") 278 if err != nil { 279 return err 280 } 281 reply.Index = index 282 283 // Set the query response 284 j.srv.setQueryMeta(&reply.QueryMeta) 285 return nil 286 } 287 288 // Evaluations is used to list the evaluations for a job 289 func (j *Job) Evaluations(args *structs.JobSpecificRequest, 290 reply *structs.JobEvaluationsResponse) error { 291 if done, err := j.srv.forward("Job.Evaluations", args, args, reply); done { 292 return err 293 } 294 defer metrics.MeasureSince([]string{"nomad", "job", "evaluations"}, time.Now()) 295 296 // Capture the evaluations 297 snap, err := j.srv.fsm.State().Snapshot() 298 if err != nil { 299 return err 300 } 301 reply.Evaluations, err = snap.EvalsByJob(args.JobID) 302 if err != nil { 303 return err 304 } 305 306 // Use the last index that affected the evals table 307 index, err := snap.Index("evals") 308 if err != nil { 309 return err 310 } 311 reply.Index = index 312 313 // Set the query response 314 j.srv.setQueryMeta(&reply.QueryMeta) 315 return nil 316 }