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