github.com/diptanu/nomad@v0.5.7-0.20170516172507-d72e86cbe3d9/nomad/eval_endpoint.go (about) 1 package nomad 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/armon/go-metrics" 8 "github.com/hashicorp/go-memdb" 9 "github.com/hashicorp/nomad/nomad/state" 10 "github.com/hashicorp/nomad/nomad/structs" 11 "github.com/hashicorp/nomad/scheduler" 12 ) 13 14 const ( 15 // DefaultDequeueTimeout is used if no dequeue timeout is provided 16 DefaultDequeueTimeout = time.Second 17 ) 18 19 // Eval endpoint is used for eval interactions 20 type Eval struct { 21 srv *Server 22 } 23 24 // GetEval is used to request information about a specific evaluation 25 func (e *Eval) GetEval(args *structs.EvalSpecificRequest, 26 reply *structs.SingleEvalResponse) error { 27 if done, err := e.srv.forward("Eval.GetEval", args, args, reply); done { 28 return err 29 } 30 defer metrics.MeasureSince([]string{"nomad", "eval", "get_eval"}, time.Now()) 31 32 // Setup the blocking query 33 opts := blockingOptions{ 34 queryOpts: &args.QueryOptions, 35 queryMeta: &reply.QueryMeta, 36 run: func(ws memdb.WatchSet, state *state.StateStore) error { 37 // Look for the job 38 out, err := state.EvalByID(ws, args.EvalID) 39 if err != nil { 40 return err 41 } 42 43 // Setup the output 44 reply.Eval = out 45 if out != nil { 46 reply.Index = out.ModifyIndex 47 } else { 48 // Use the last index that affected the nodes table 49 index, err := state.Index("evals") 50 if err != nil { 51 return err 52 } 53 reply.Index = index 54 } 55 56 // Set the query response 57 e.srv.setQueryMeta(&reply.QueryMeta) 58 return nil 59 }} 60 return e.srv.blockingRPC(&opts) 61 } 62 63 // Dequeue is used to dequeue a pending evaluation 64 func (e *Eval) Dequeue(args *structs.EvalDequeueRequest, 65 reply *structs.EvalDequeueResponse) error { 66 if done, err := e.srv.forward("Eval.Dequeue", args, args, reply); done { 67 return err 68 } 69 defer metrics.MeasureSince([]string{"nomad", "eval", "dequeue"}, time.Now()) 70 71 // Ensure there is at least one scheduler 72 if len(args.Schedulers) == 0 { 73 return fmt.Errorf("dequeue requires at least one scheduler type") 74 } 75 76 // Check that there isn't a scheduler version mismatch 77 if args.SchedulerVersion != scheduler.SchedulerVersion { 78 return fmt.Errorf("dequeue disallowed: calling scheduler version is %d; leader version is %d", 79 args.SchedulerVersion, scheduler.SchedulerVersion) 80 } 81 82 // Ensure there is a default timeout 83 if args.Timeout <= 0 { 84 args.Timeout = DefaultDequeueTimeout 85 } 86 87 // Attempt the dequeue 88 eval, token, err := e.srv.evalBroker.Dequeue(args.Schedulers, args.Timeout) 89 if err != nil { 90 return err 91 } 92 93 // Provide the output if any 94 if eval != nil { 95 reply.Eval = eval 96 reply.Token = token 97 } 98 99 // Set the query response 100 e.srv.setQueryMeta(&reply.QueryMeta) 101 return nil 102 } 103 104 // Ack is used to acknowledge completion of a dequeued evaluation 105 func (e *Eval) Ack(args *structs.EvalAckRequest, 106 reply *structs.GenericResponse) error { 107 if done, err := e.srv.forward("Eval.Ack", args, args, reply); done { 108 return err 109 } 110 defer metrics.MeasureSince([]string{"nomad", "eval", "ack"}, time.Now()) 111 112 // Ack the EvalID 113 if err := e.srv.evalBroker.Ack(args.EvalID, args.Token); err != nil { 114 return err 115 } 116 return nil 117 } 118 119 // NAck is used to negative acknowledge completion of a dequeued evaluation 120 func (e *Eval) Nack(args *structs.EvalAckRequest, 121 reply *structs.GenericResponse) error { 122 if done, err := e.srv.forward("Eval.Nack", args, args, reply); done { 123 return err 124 } 125 defer metrics.MeasureSince([]string{"nomad", "eval", "nack"}, time.Now()) 126 127 // Nack the EvalID 128 if err := e.srv.evalBroker.Nack(args.EvalID, args.Token); err != nil { 129 return err 130 } 131 return nil 132 } 133 134 // Update is used to perform an update of an Eval if it is outstanding. 135 func (e *Eval) Update(args *structs.EvalUpdateRequest, 136 reply *structs.GenericResponse) error { 137 if done, err := e.srv.forward("Eval.Update", args, args, reply); done { 138 return err 139 } 140 defer metrics.MeasureSince([]string{"nomad", "eval", "update"}, time.Now()) 141 142 // Ensure there is only a single update with token 143 if len(args.Evals) != 1 { 144 return fmt.Errorf("only a single eval can be updated") 145 } 146 eval := args.Evals[0] 147 148 // Verify the evaluation is outstanding, and that the tokens match. 149 if err := e.srv.evalBroker.OutstandingReset(eval.ID, args.EvalToken); err != nil { 150 return err 151 } 152 153 // Update via Raft 154 _, index, err := e.srv.raftApply(structs.EvalUpdateRequestType, args) 155 if err != nil { 156 return err 157 } 158 159 // Update the index 160 reply.Index = index 161 return nil 162 } 163 164 // Create is used to make a new evaluation 165 func (e *Eval) Create(args *structs.EvalUpdateRequest, 166 reply *structs.GenericResponse) error { 167 if done, err := e.srv.forward("Eval.Create", args, args, reply); done { 168 return err 169 } 170 defer metrics.MeasureSince([]string{"nomad", "eval", "create"}, time.Now()) 171 172 // Ensure there is only a single update with token 173 if len(args.Evals) != 1 { 174 return fmt.Errorf("only a single eval can be created") 175 } 176 eval := args.Evals[0] 177 178 // Verify the parent evaluation is outstanding, and that the tokens match. 179 if err := e.srv.evalBroker.OutstandingReset(eval.PreviousEval, args.EvalToken); err != nil { 180 return err 181 } 182 183 // Look for the eval 184 snap, err := e.srv.fsm.State().Snapshot() 185 if err != nil { 186 return err 187 } 188 189 ws := memdb.NewWatchSet() 190 out, err := snap.EvalByID(ws, eval.ID) 191 if err != nil { 192 return err 193 } 194 if out != nil { 195 return fmt.Errorf("evaluation already exists") 196 } 197 198 // Update via Raft 199 _, index, err := e.srv.raftApply(structs.EvalUpdateRequestType, args) 200 if err != nil { 201 return err 202 } 203 204 // Update the index 205 reply.Index = index 206 return nil 207 } 208 209 // Reblock is used to reinsert an existing blocked evaluation into the blocked 210 // evaluation tracker. 211 func (e *Eval) Reblock(args *structs.EvalUpdateRequest, reply *structs.GenericResponse) error { 212 if done, err := e.srv.forward("Eval.Reblock", args, args, reply); done { 213 return err 214 } 215 defer metrics.MeasureSince([]string{"nomad", "eval", "reblock"}, time.Now()) 216 217 // Ensure there is only a single update with token 218 if len(args.Evals) != 1 { 219 return fmt.Errorf("only a single eval can be reblocked") 220 } 221 eval := args.Evals[0] 222 223 // Verify the evaluation is outstanding, and that the tokens match. 224 if err := e.srv.evalBroker.OutstandingReset(eval.ID, args.EvalToken); err != nil { 225 return err 226 } 227 228 // Look for the eval 229 snap, err := e.srv.fsm.State().Snapshot() 230 if err != nil { 231 return err 232 } 233 234 ws := memdb.NewWatchSet() 235 out, err := snap.EvalByID(ws, eval.ID) 236 if err != nil { 237 return err 238 } 239 if out == nil { 240 return fmt.Errorf("evaluation does not exist") 241 } 242 if out.Status != structs.EvalStatusBlocked { 243 return fmt.Errorf("evaluation not blocked") 244 } 245 246 // Reblock the eval 247 e.srv.blockedEvals.Reblock(eval, args.EvalToken) 248 return nil 249 } 250 251 // Reap is used to cleanup dead evaluations and allocations 252 func (e *Eval) Reap(args *structs.EvalDeleteRequest, 253 reply *structs.GenericResponse) error { 254 if done, err := e.srv.forward("Eval.Reap", args, args, reply); done { 255 return err 256 } 257 defer metrics.MeasureSince([]string{"nomad", "eval", "reap"}, time.Now()) 258 259 // Update via Raft 260 _, index, err := e.srv.raftApply(structs.EvalDeleteRequestType, args) 261 if err != nil { 262 return err 263 } 264 265 // Update the index 266 reply.Index = index 267 return nil 268 } 269 270 // List is used to get a list of the evaluations in the system 271 func (e *Eval) List(args *structs.EvalListRequest, 272 reply *structs.EvalListResponse) error { 273 if done, err := e.srv.forward("Eval.List", args, args, reply); done { 274 return err 275 } 276 defer metrics.MeasureSince([]string{"nomad", "eval", "list"}, time.Now()) 277 278 // Setup the blocking query 279 opts := blockingOptions{ 280 queryOpts: &args.QueryOptions, 281 queryMeta: &reply.QueryMeta, 282 run: func(ws memdb.WatchSet, state *state.StateStore) error { 283 // Scan all the evaluations 284 var err error 285 var iter memdb.ResultIterator 286 if prefix := args.QueryOptions.Prefix; prefix != "" { 287 iter, err = state.EvalsByIDPrefix(ws, prefix) 288 } else { 289 iter, err = state.Evals(ws) 290 } 291 if err != nil { 292 return err 293 } 294 295 var evals []*structs.Evaluation 296 for { 297 raw := iter.Next() 298 if raw == nil { 299 break 300 } 301 eval := raw.(*structs.Evaluation) 302 evals = append(evals, eval) 303 } 304 reply.Evaluations = evals 305 306 // Use the last index that affected the jobs table 307 index, err := state.Index("evals") 308 if err != nil { 309 return err 310 } 311 reply.Index = index 312 313 // Set the query response 314 e.srv.setQueryMeta(&reply.QueryMeta) 315 return nil 316 }} 317 return e.srv.blockingRPC(&opts) 318 } 319 320 // Allocations is used to list the allocations for an evaluation 321 func (e *Eval) Allocations(args *structs.EvalSpecificRequest, 322 reply *structs.EvalAllocationsResponse) error { 323 if done, err := e.srv.forward("Eval.Allocations", args, args, reply); done { 324 return err 325 } 326 defer metrics.MeasureSince([]string{"nomad", "eval", "allocations"}, time.Now()) 327 328 // Setup the blocking query 329 opts := blockingOptions{ 330 queryOpts: &args.QueryOptions, 331 queryMeta: &reply.QueryMeta, 332 run: func(ws memdb.WatchSet, state *state.StateStore) error { 333 // Capture the allocations 334 allocs, err := state.AllocsByEval(ws, args.EvalID) 335 if err != nil { 336 return err 337 } 338 339 // Convert to a stub 340 if len(allocs) > 0 { 341 reply.Allocations = make([]*structs.AllocListStub, 0, len(allocs)) 342 for _, alloc := range allocs { 343 reply.Allocations = append(reply.Allocations, alloc.Stub()) 344 } 345 } 346 347 // Use the last index that affected the allocs table 348 index, err := state.Index("allocs") 349 if err != nil { 350 return err 351 } 352 reply.Index = index 353 354 // Set the query response 355 e.srv.setQueryMeta(&reply.QueryMeta) 356 return nil 357 }} 358 return e.srv.blockingRPC(&opts) 359 }