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 }