github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/nomad/alloc_endpoint.go (about)

     1  package nomad
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/armon/go-metrics"
     7  	"github.com/hashicorp/go-memdb"
     8  	multierror "github.com/hashicorp/go-multierror"
     9  	"github.com/hashicorp/nomad/acl"
    10  	"github.com/hashicorp/nomad/nomad/state"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  )
    13  
    14  // Alloc endpoint is used for manipulating allocations
    15  type Alloc struct {
    16  	srv *Server
    17  }
    18  
    19  // List is used to list the allocations in the system
    20  func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListResponse) error {
    21  	if done, err := a.srv.forward("Alloc.List", args, args, reply); done {
    22  		return err
    23  	}
    24  	defer metrics.MeasureSince([]string{"nomad", "alloc", "list"}, time.Now())
    25  
    26  	// Check namespace read-job permissions
    27  	if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
    28  		return err
    29  	} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
    30  		return structs.ErrPermissionDenied
    31  	}
    32  
    33  	// Setup the blocking query
    34  	opts := blockingOptions{
    35  		queryOpts: &args.QueryOptions,
    36  		queryMeta: &reply.QueryMeta,
    37  		run: func(ws memdb.WatchSet, state *state.StateStore) error {
    38  			// Capture all the allocations
    39  			var err error
    40  			var iter memdb.ResultIterator
    41  			if prefix := args.QueryOptions.Prefix; prefix != "" {
    42  				iter, err = state.AllocsByIDPrefix(ws, args.RequestNamespace(), prefix)
    43  			} else {
    44  				iter, err = state.AllocsByNamespace(ws, args.RequestNamespace())
    45  			}
    46  			if err != nil {
    47  				return err
    48  			}
    49  
    50  			var allocs []*structs.AllocListStub
    51  			for {
    52  				raw := iter.Next()
    53  				if raw == nil {
    54  					break
    55  				}
    56  				alloc := raw.(*structs.Allocation)
    57  				allocs = append(allocs, alloc.Stub())
    58  			}
    59  			reply.Allocations = allocs
    60  
    61  			// Use the last index that affected the jobs table
    62  			index, err := state.Index("allocs")
    63  			if err != nil {
    64  				return err
    65  			}
    66  			reply.Index = index
    67  
    68  			// Set the query response
    69  			a.srv.setQueryMeta(&reply.QueryMeta)
    70  			return nil
    71  		}}
    72  	return a.srv.blockingRPC(&opts)
    73  }
    74  
    75  // GetAlloc is used to lookup a particular allocation
    76  func (a *Alloc) GetAlloc(args *structs.AllocSpecificRequest,
    77  	reply *structs.SingleAllocResponse) error {
    78  	if done, err := a.srv.forward("Alloc.GetAlloc", args, args, reply); done {
    79  		return err
    80  	}
    81  	defer metrics.MeasureSince([]string{"nomad", "alloc", "get_alloc"}, time.Now())
    82  
    83  	// Check namespace read-job permissions
    84  	if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
    85  		// If ResolveToken had an unexpected error return that
    86  		if err != structs.ErrTokenNotFound {
    87  			return err
    88  		}
    89  
    90  		// Attempt to lookup AuthToken as a Node.SecretID since nodes
    91  		// call this endpoint and don't have an ACL token.
    92  		node, stateErr := a.srv.fsm.State().NodeBySecretID(nil, args.AuthToken)
    93  		if stateErr != nil {
    94  			// Return the original ResolveToken error with this err
    95  			var merr multierror.Error
    96  			merr.Errors = append(merr.Errors, err, stateErr)
    97  			return merr.ErrorOrNil()
    98  		}
    99  
   100  		// Not a node or a valid ACL token
   101  		if node == nil {
   102  			return structs.ErrTokenNotFound
   103  		}
   104  	} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
   105  		return structs.ErrPermissionDenied
   106  	}
   107  
   108  	// Setup the blocking query
   109  	opts := blockingOptions{
   110  		queryOpts: &args.QueryOptions,
   111  		queryMeta: &reply.QueryMeta,
   112  		run: func(ws memdb.WatchSet, state *state.StateStore) error {
   113  			// Lookup the allocation
   114  			out, err := state.AllocByID(ws, args.AllocID)
   115  			if err != nil {
   116  				return err
   117  			}
   118  
   119  			// Setup the output
   120  			reply.Alloc = out
   121  			if out != nil {
   122  				reply.Index = out.ModifyIndex
   123  			} else {
   124  				// Use the last index that affected the allocs table
   125  				index, err := state.Index("allocs")
   126  				if err != nil {
   127  					return err
   128  				}
   129  				reply.Index = index
   130  			}
   131  
   132  			// Set the query response
   133  			a.srv.setQueryMeta(&reply.QueryMeta)
   134  			return nil
   135  		}}
   136  	return a.srv.blockingRPC(&opts)
   137  }
   138  
   139  // GetAllocs is used to lookup a set of allocations
   140  func (a *Alloc) GetAllocs(args *structs.AllocsGetRequest,
   141  	reply *structs.AllocsGetResponse) error {
   142  	if done, err := a.srv.forward("Alloc.GetAllocs", args, args, reply); done {
   143  		return err
   144  	}
   145  	defer metrics.MeasureSince([]string{"nomad", "alloc", "get_allocs"}, time.Now())
   146  
   147  	allocs := make([]*structs.Allocation, len(args.AllocIDs))
   148  
   149  	// Setup the blocking query. We wait for at least one of the requested
   150  	// allocations to be above the min query index. This guarantees that the
   151  	// server has received that index.
   152  	opts := blockingOptions{
   153  		queryOpts: &args.QueryOptions,
   154  		queryMeta: &reply.QueryMeta,
   155  		run: func(ws memdb.WatchSet, state *state.StateStore) error {
   156  			// Lookup the allocation
   157  			thresholdMet := false
   158  			maxIndex := uint64(0)
   159  			for i, alloc := range args.AllocIDs {
   160  				out, err := state.AllocByID(ws, alloc)
   161  				if err != nil {
   162  					return err
   163  				}
   164  				if out == nil {
   165  					// We don't have the alloc yet
   166  					thresholdMet = false
   167  					break
   168  				}
   169  
   170  				// Store the pointer
   171  				allocs[i] = out
   172  
   173  				// Check if we have passed the minimum index
   174  				if out.ModifyIndex > args.QueryOptions.MinQueryIndex {
   175  					thresholdMet = true
   176  				}
   177  
   178  				if maxIndex < out.ModifyIndex {
   179  					maxIndex = out.ModifyIndex
   180  				}
   181  			}
   182  
   183  			// Setup the output
   184  			if thresholdMet {
   185  				reply.Allocs = allocs
   186  				reply.Index = maxIndex
   187  			} else {
   188  				// Use the last index that affected the nodes table
   189  				index, err := state.Index("allocs")
   190  				if err != nil {
   191  					return err
   192  				}
   193  				reply.Index = index
   194  			}
   195  
   196  			// Set the query response
   197  			a.srv.setQueryMeta(&reply.QueryMeta)
   198  			return nil
   199  		},
   200  	}
   201  	return a.srv.blockingRPC(&opts)
   202  }