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 }