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