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