2015-07-23 23:00:19 +00:00
|
|
|
package nomad
|
|
|
|
|
|
|
|
import (
|
2015-07-24 04:58:51 +00:00
|
|
|
"fmt"
|
2015-07-23 23:00:19 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/armon/go-metrics"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
|
|
)
|
|
|
|
|
2015-07-24 04:58:51 +00:00
|
|
|
const (
|
|
|
|
// DefaultDequeueTimeout is used if no dequeue timeout is provided
|
|
|
|
DefaultDequeueTimeout = time.Second
|
|
|
|
)
|
|
|
|
|
2015-07-23 23:00:19 +00:00
|
|
|
// Eval endpoint is used for eval interactions
|
|
|
|
type Eval struct {
|
|
|
|
srv *Server
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetEval is used to request information about a specific evaluation
|
|
|
|
func (e *Eval) GetEval(args *structs.EvalSpecificRequest,
|
|
|
|
reply *structs.SingleEvalResponse) error {
|
|
|
|
if done, err := e.srv.forward("Eval.GetEval", args, args, reply); done {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer metrics.MeasureSince([]string{"nomad", "eval", "get_eval"}, time.Now())
|
|
|
|
|
|
|
|
// Look for the job
|
|
|
|
snap, err := e.srv.fsm.State().Snapshot()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
out, err := snap.GetEvalByID(args.EvalID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the output
|
|
|
|
if out != nil {
|
|
|
|
reply.Eval = out
|
|
|
|
reply.Index = out.ModifyIndex
|
|
|
|
} else {
|
|
|
|
// Use the last index that affected the nodes table
|
|
|
|
index, err := snap.GetIndex("evals")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
reply.Index = index
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the query response
|
|
|
|
e.srv.setQueryMeta(&reply.QueryMeta)
|
|
|
|
return nil
|
|
|
|
}
|
2015-07-24 04:58:51 +00:00
|
|
|
|
|
|
|
// Dequeue is used to dequeue a pending evaluation
|
|
|
|
func (e *Eval) Dequeue(args *structs.EvalDequeueRequest,
|
2015-08-12 22:25:31 +00:00
|
|
|
reply *structs.EvalDequeueResponse) error {
|
2015-07-28 22:01:29 +00:00
|
|
|
if done, err := e.srv.forward("Eval.Dequeue", args, args, reply); done {
|
2015-07-24 04:58:51 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-07-24 05:11:25 +00:00
|
|
|
defer metrics.MeasureSince([]string{"nomad", "eval", "dequeue"}, time.Now())
|
2015-07-24 04:58:51 +00:00
|
|
|
|
|
|
|
// Ensure there is at least one scheduler
|
|
|
|
if len(args.Schedulers) == 0 {
|
|
|
|
return fmt.Errorf("dequeue requires at least one scheduler type")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure there is a default timeout
|
|
|
|
if args.Timeout <= 0 {
|
|
|
|
args.Timeout = DefaultDequeueTimeout
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt the dequeue
|
2015-08-12 22:25:31 +00:00
|
|
|
eval, token, err := e.srv.evalBroker.Dequeue(args.Schedulers, args.Timeout)
|
2015-07-24 04:58:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Provide the output if any
|
|
|
|
if eval != nil {
|
|
|
|
reply.Eval = eval
|
2015-08-12 22:25:31 +00:00
|
|
|
reply.Token = token
|
2015-07-24 04:58:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set the query response
|
|
|
|
e.srv.setQueryMeta(&reply.QueryMeta)
|
|
|
|
return nil
|
|
|
|
}
|
2015-07-24 05:11:25 +00:00
|
|
|
|
|
|
|
// Ack is used to acknowledge completion of a dequeued evaluation
|
2015-08-12 22:25:31 +00:00
|
|
|
func (e *Eval) Ack(args *structs.EvalAckRequest,
|
2015-07-24 05:11:25 +00:00
|
|
|
reply *structs.GenericResponse) error {
|
|
|
|
if done, err := e.srv.forward("Eval.Ack", args, args, reply); done {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer metrics.MeasureSince([]string{"nomad", "eval", "ack"}, time.Now())
|
|
|
|
|
|
|
|
// Ack the EvalID
|
2015-08-12 22:25:31 +00:00
|
|
|
if err := e.srv.evalBroker.Ack(args.EvalID, args.Token); err != nil {
|
2015-07-24 05:11:25 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NAck is used to negative acknowledge completion of a dequeued evaluation
|
2015-08-12 22:25:31 +00:00
|
|
|
func (e *Eval) Nack(args *structs.EvalAckRequest,
|
2015-07-24 05:11:25 +00:00
|
|
|
reply *structs.GenericResponse) error {
|
|
|
|
if done, err := e.srv.forward("Eval.Nack", args, args, reply); done {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer metrics.MeasureSince([]string{"nomad", "eval", "nack"}, time.Now())
|
|
|
|
|
|
|
|
// Nack the EvalID
|
2015-08-12 22:25:31 +00:00
|
|
|
if err := e.srv.evalBroker.Nack(args.EvalID, args.Token); err != nil {
|
2015-07-24 05:11:25 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2015-08-15 21:16:40 +00:00
|
|
|
|
|
|
|
// Update is used to perform an update of an Eval if it is outstanding.
|
|
|
|
func (e *Eval) Update(args *structs.EvalUpdateRequest,
|
|
|
|
reply *structs.GenericResponse) error {
|
|
|
|
if done, err := e.srv.forward("Eval.Update", args, args, reply); done {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer metrics.MeasureSince([]string{"nomad", "eval", "update"}, time.Now())
|
|
|
|
|
|
|
|
// Ensure there is only a single update with token
|
|
|
|
if len(args.Evals) != 1 {
|
|
|
|
return fmt.Errorf("only a single eval can be updated")
|
|
|
|
}
|
|
|
|
eval := args.Evals[0]
|
|
|
|
|
|
|
|
// Verify the evaluation is outstanding, and that the tokens match.
|
|
|
|
token, ok := e.srv.evalBroker.Outstanding(eval.ID)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("evaluation is not outstanding")
|
|
|
|
}
|
2015-08-15 21:22:21 +00:00
|
|
|
if args.EvalToken != token {
|
2015-08-15 21:16:40 +00:00
|
|
|
return fmt.Errorf("evaluation token does not match")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update via Raft
|
|
|
|
_, index, err := e.srv.raftApply(structs.EvalUpdateRequestType, args)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the index
|
|
|
|
reply.Index = index
|
|
|
|
return nil
|
|
|
|
}
|