github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/command/agent/agent_endpoint.go (about) 1 package agent 2 3 import ( 4 "encoding/json" 5 "net" 6 "net/http" 7 "sort" 8 "strings" 9 10 "github.com/hashicorp/nomad/acl" 11 "github.com/hashicorp/nomad/nomad/structs" 12 "github.com/hashicorp/serf/serf" 13 "github.com/mitchellh/copystructure" 14 ) 15 16 type Member struct { 17 Name string 18 Addr net.IP 19 Port uint16 20 Tags map[string]string 21 Status string 22 ProtocolMin uint8 23 ProtocolMax uint8 24 ProtocolCur uint8 25 DelegateMin uint8 26 DelegateMax uint8 27 DelegateCur uint8 28 } 29 30 func nomadMember(m serf.Member) Member { 31 return Member{ 32 Name: m.Name, 33 Addr: m.Addr, 34 Port: m.Port, 35 Tags: m.Tags, 36 Status: m.Status.String(), 37 ProtocolMin: m.ProtocolMin, 38 ProtocolMax: m.ProtocolMax, 39 ProtocolCur: m.ProtocolCur, 40 DelegateMin: m.DelegateMin, 41 DelegateMax: m.DelegateMax, 42 DelegateCur: m.DelegateCur, 43 } 44 } 45 46 func (s *HTTPServer) AgentSelfRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 47 if req.Method != "GET" { 48 return nil, CodedError(405, ErrInvalidMethod) 49 } 50 51 var secret string 52 s.parseToken(req, &secret) 53 54 var aclObj *acl.ACL 55 var err error 56 57 // Get the member as a server 58 var member serf.Member 59 if srv := s.agent.Server(); srv != nil { 60 member = srv.LocalMember() 61 aclObj, err = srv.ResolveToken(secret) 62 } else { 63 // Not a Server; use the Client for token resolution 64 aclObj, err = s.agent.Client().ResolveToken(secret) 65 } 66 67 if err != nil { 68 return nil, err 69 } 70 71 // Check agent read permissions 72 if aclObj != nil && !aclObj.AllowAgentRead() { 73 return nil, structs.ErrPermissionDenied 74 } 75 76 self := agentSelf{ 77 Member: nomadMember(member), 78 Stats: s.agent.Stats(), 79 } 80 if ac, err := copystructure.Copy(s.agent.config); err != nil { 81 return nil, CodedError(500, err.Error()) 82 } else { 83 self.Config = ac.(*Config) 84 } 85 86 if self.Config != nil && self.Config.Vault != nil && self.Config.Vault.Token != "" { 87 self.Config.Vault.Token = "<redacted>" 88 } 89 90 return self, nil 91 } 92 93 func (s *HTTPServer) AgentJoinRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 94 if req.Method != "PUT" && req.Method != "POST" { 95 return nil, CodedError(405, ErrInvalidMethod) 96 } 97 srv := s.agent.Server() 98 if srv == nil { 99 return nil, CodedError(501, ErrInvalidMethod) 100 } 101 102 // Get the join addresses 103 query := req.URL.Query() 104 addrs := query["address"] 105 if len(addrs) == 0 { 106 return nil, CodedError(400, "missing address to join") 107 } 108 109 // Attempt the join 110 num, err := srv.Join(addrs) 111 var errStr string 112 if err != nil { 113 errStr = err.Error() 114 } 115 return joinResult{num, errStr}, nil 116 } 117 118 func (s *HTTPServer) AgentMembersRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 119 if req.Method != "GET" { 120 return nil, CodedError(405, ErrInvalidMethod) 121 } 122 123 args := &structs.GenericRequest{} 124 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 125 return nil, nil 126 } 127 128 var out structs.ServerMembersResponse 129 if err := s.agent.RPC("Status.Members", args, &out); err != nil { 130 return nil, err 131 } 132 133 return out, nil 134 } 135 136 func (s *HTTPServer) AgentForceLeaveRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 137 if req.Method != "PUT" && req.Method != "POST" { 138 return nil, CodedError(405, ErrInvalidMethod) 139 } 140 srv := s.agent.Server() 141 if srv == nil { 142 return nil, CodedError(501, ErrInvalidMethod) 143 } 144 145 var secret string 146 s.parseToken(req, &secret) 147 148 // Check agent write permissions 149 if aclObj, err := s.agent.Server().ResolveToken(secret); err != nil { 150 return nil, err 151 } else if aclObj != nil && !aclObj.AllowAgentWrite() { 152 return nil, structs.ErrPermissionDenied 153 } 154 155 // Get the node to eject 156 node := req.URL.Query().Get("node") 157 if node == "" { 158 return nil, CodedError(400, "missing node to force leave") 159 } 160 161 // Attempt remove 162 err := srv.RemoveFailedNode(node) 163 return nil, err 164 } 165 166 // AgentServersRequest is used to query the list of servers used by the Nomad 167 // Client for RPCs. This endpoint can also be used to update the list of 168 // servers for a given agent. 169 func (s *HTTPServer) AgentServersRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 170 switch req.Method { 171 case "PUT", "POST": 172 return s.updateServers(resp, req) 173 case "GET": 174 return s.listServers(resp, req) 175 default: 176 return nil, CodedError(405, ErrInvalidMethod) 177 } 178 } 179 180 func (s *HTTPServer) listServers(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 181 client := s.agent.Client() 182 if client == nil { 183 return nil, CodedError(501, ErrInvalidMethod) 184 } 185 186 var secret string 187 s.parseToken(req, &secret) 188 189 // Check agent read permissions 190 if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil { 191 return nil, err 192 } else if aclObj != nil && !aclObj.AllowAgentRead() { 193 return nil, structs.ErrPermissionDenied 194 } 195 196 peers := s.agent.client.GetServers() 197 sort.Strings(peers) 198 return peers, nil 199 } 200 201 func (s *HTTPServer) updateServers(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 202 client := s.agent.Client() 203 if client == nil { 204 return nil, CodedError(501, ErrInvalidMethod) 205 } 206 207 // Get the servers from the request 208 servers := req.URL.Query()["address"] 209 if len(servers) == 0 { 210 return nil, CodedError(400, "missing server address") 211 } 212 213 var secret string 214 s.parseToken(req, &secret) 215 216 // Check agent write permissions 217 if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil { 218 return nil, err 219 } else if aclObj != nil && !aclObj.AllowAgentWrite() { 220 return nil, structs.ErrPermissionDenied 221 } 222 223 // Set the servers list into the client 224 s.agent.logger.Printf("[TRACE] Adding servers %+q to the client's primary server list", servers) 225 if err := client.SetServers(servers); err != nil { 226 s.agent.logger.Printf("[ERR] Attempt to add servers %q to client failed: %v", servers, err) 227 //TODO is this the right error to return? 228 return nil, CodedError(400, err.Error()) 229 } 230 return nil, nil 231 } 232 233 // KeyringOperationRequest allows an operator to install/delete/use keys 234 func (s *HTTPServer) KeyringOperationRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 235 srv := s.agent.Server() 236 if srv == nil { 237 return nil, CodedError(501, ErrInvalidMethod) 238 } 239 240 var secret string 241 s.parseToken(req, &secret) 242 243 // Check agent write permissions 244 if aclObj, err := srv.ResolveToken(secret); err != nil { 245 return nil, err 246 } else if aclObj != nil && !aclObj.AllowAgentWrite() { 247 return nil, structs.ErrPermissionDenied 248 } 249 250 kmgr := srv.KeyManager() 251 var sresp *serf.KeyResponse 252 var err error 253 254 // Get the key from the req body 255 var args structs.KeyringRequest 256 257 //Get the op 258 op := strings.TrimPrefix(req.URL.Path, "/v1/agent/keyring/") 259 260 switch op { 261 case "list": 262 sresp, err = kmgr.ListKeys() 263 case "install": 264 if err := decodeBody(req, &args); err != nil { 265 return nil, CodedError(500, err.Error()) 266 } 267 sresp, err = kmgr.InstallKey(args.Key) 268 case "use": 269 if err := decodeBody(req, &args); err != nil { 270 return nil, CodedError(500, err.Error()) 271 } 272 sresp, err = kmgr.UseKey(args.Key) 273 case "remove": 274 if err := decodeBody(req, &args); err != nil { 275 return nil, CodedError(500, err.Error()) 276 } 277 sresp, err = kmgr.RemoveKey(args.Key) 278 default: 279 return nil, CodedError(404, "resource not found") 280 } 281 282 if err != nil { 283 return nil, err 284 } 285 kresp := structs.KeyringResponse{ 286 Messages: sresp.Messages, 287 Keys: sresp.Keys, 288 NumNodes: sresp.NumNodes, 289 } 290 return kresp, nil 291 } 292 293 type agentSelf struct { 294 Config *Config `json:"config"` 295 Member Member `json:"member,omitempty"` 296 Stats map[string]map[string]string `json:"stats"` 297 } 298 299 type joinResult struct { 300 NumJoined int `json:"num_joined"` 301 Error string `json:"error"` 302 } 303 304 func (s *HTTPServer) HealthRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 305 if req.Method != "GET" { 306 return nil, CodedError(405, ErrInvalidMethod) 307 } 308 309 var args structs.GenericRequest 310 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 311 return nil, nil 312 } 313 314 health := healthResponse{} 315 getClient := true 316 getServer := true 317 318 // See if we're checking a specific agent type and default to failing 319 if healthType, ok := req.URL.Query()["type"]; ok { 320 getClient = false 321 getServer = false 322 for _, ht := range healthType { 323 switch ht { 324 case "client": 325 getClient = true 326 health.Client = &healthResponseAgent{ 327 Ok: false, 328 Message: "client not enabled", 329 } 330 case "server": 331 getServer = true 332 health.Server = &healthResponseAgent{ 333 Ok: false, 334 Message: "server not enabled", 335 } 336 } 337 } 338 } 339 340 // If we should check the client and it exists assume it's healthy 341 if client := s.agent.Client(); getClient && client != nil { 342 if len(client.GetServers()) == 0 { 343 health.Client = &healthResponseAgent{ 344 Ok: false, 345 Message: "no known servers", 346 } 347 } else { 348 health.Client = &healthResponseAgent{ 349 Ok: true, 350 Message: "ok", 351 } 352 } 353 } 354 355 // If we should check the server and it exists, see if there's a leader 356 if server := s.agent.Server(); getServer && server != nil { 357 health.Server = &healthResponseAgent{ 358 Ok: true, 359 Message: "ok", 360 } 361 362 leader := "" 363 if err := s.agent.RPC("Status.Leader", &args, &leader); err != nil { 364 health.Server.Ok = false 365 health.Server.Message = err.Error() 366 } else if leader == "" { 367 health.Server.Ok = false 368 health.Server.Message = "no leader" 369 } 370 } 371 372 if health.ok() { 373 return &health, nil 374 } 375 376 jsonResp, err := json.Marshal(&health) 377 if err != nil { 378 return nil, err 379 } 380 return nil, CodedError(500, string(jsonResp)) 381 } 382 383 type healthResponse struct { 384 Client *healthResponseAgent `json:"client,omitempty"` 385 Server *healthResponseAgent `json:"server,omitempty"` 386 } 387 388 // ok returns true as long as neither Client nor Server have Ok=false. 389 func (h healthResponse) ok() bool { 390 if h.Client != nil && !h.Client.Ok { 391 return false 392 } 393 if h.Server != nil && !h.Server.Ok { 394 return false 395 } 396 return true 397 } 398 399 type healthResponseAgent struct { 400 Ok bool `json:"ok"` 401 Message string `json:"message,omitempty"` 402 }