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  }