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  }