github.com/karlem/nomad@v0.10.2-rc1/command/agent/agent_endpoint.go (about) 1 package agent 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "net" 10 "net/http" 11 "sort" 12 "strconv" 13 "strings" 14 15 "github.com/docker/docker/pkg/ioutils" 16 log "github.com/hashicorp/go-hclog" 17 "github.com/hashicorp/nomad/acl" 18 cstructs "github.com/hashicorp/nomad/client/structs" 19 "github.com/hashicorp/nomad/nomad/structs" 20 "github.com/hashicorp/serf/serf" 21 "github.com/mitchellh/copystructure" 22 "github.com/ugorji/go/codec" 23 ) 24 25 type Member struct { 26 Name string 27 Addr net.IP 28 Port uint16 29 Tags map[string]string 30 Status string 31 ProtocolMin uint8 32 ProtocolMax uint8 33 ProtocolCur uint8 34 DelegateMin uint8 35 DelegateMax uint8 36 DelegateCur uint8 37 } 38 39 func nomadMember(m serf.Member) Member { 40 return Member{ 41 Name: m.Name, 42 Addr: m.Addr, 43 Port: m.Port, 44 Tags: m.Tags, 45 Status: m.Status.String(), 46 ProtocolMin: m.ProtocolMin, 47 ProtocolMax: m.ProtocolMax, 48 ProtocolCur: m.ProtocolCur, 49 DelegateMin: m.DelegateMin, 50 DelegateMax: m.DelegateMax, 51 DelegateCur: m.DelegateCur, 52 } 53 } 54 55 func (s *HTTPServer) AgentSelfRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 56 if req.Method != "GET" { 57 return nil, CodedError(405, ErrInvalidMethod) 58 } 59 60 var secret string 61 s.parseToken(req, &secret) 62 63 var aclObj *acl.ACL 64 var err error 65 66 // Get the member as a server 67 var member serf.Member 68 if srv := s.agent.Server(); srv != nil { 69 member = srv.LocalMember() 70 aclObj, err = srv.ResolveToken(secret) 71 } else { 72 // Not a Server; use the Client for token resolution 73 aclObj, err = s.agent.Client().ResolveToken(secret) 74 } 75 76 if err != nil { 77 return nil, err 78 } 79 80 // Check agent read permissions 81 if aclObj != nil && !aclObj.AllowAgentRead() { 82 return nil, structs.ErrPermissionDenied 83 } 84 85 self := agentSelf{ 86 Member: nomadMember(member), 87 Stats: s.agent.Stats(), 88 } 89 if ac, err := copystructure.Copy(s.agent.config); err != nil { 90 return nil, CodedError(500, err.Error()) 91 } else { 92 self.Config = ac.(*Config) 93 } 94 95 if self.Config != nil && self.Config.Vault != nil && self.Config.Vault.Token != "" { 96 self.Config.Vault.Token = "<redacted>" 97 } 98 99 if self.Config != nil && self.Config.ACL != nil && self.Config.ACL.ReplicationToken != "" { 100 self.Config.ACL.ReplicationToken = "<redacted>" 101 } 102 103 if self.Config != nil && self.Config.Consul != nil && self.Config.Consul.Token != "" { 104 self.Config.Consul.Token = "<redacted>" 105 } 106 107 if self.Config != nil && self.Config.Telemetry != nil && self.Config.Telemetry.CirconusAPIToken != "" { 108 self.Config.Telemetry.CirconusAPIToken = "<redacted>" 109 } 110 111 return self, nil 112 } 113 114 func (s *HTTPServer) AgentJoinRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 115 if req.Method != "PUT" && req.Method != "POST" { 116 return nil, CodedError(405, ErrInvalidMethod) 117 } 118 srv := s.agent.Server() 119 if srv == nil { 120 return nil, CodedError(501, ErrInvalidMethod) 121 } 122 123 // Get the join addresses 124 query := req.URL.Query() 125 addrs := query["address"] 126 if len(addrs) == 0 { 127 return nil, CodedError(400, "missing address to join") 128 } 129 130 // Attempt the join 131 num, err := srv.Join(addrs) 132 var errStr string 133 if err != nil { 134 errStr = err.Error() 135 } 136 return joinResult{num, errStr}, nil 137 } 138 139 func (s *HTTPServer) AgentMembersRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 140 if req.Method != "GET" { 141 return nil, CodedError(405, ErrInvalidMethod) 142 } 143 144 args := &structs.GenericRequest{} 145 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 146 return nil, nil 147 } 148 149 var out structs.ServerMembersResponse 150 if err := s.agent.RPC("Status.Members", args, &out); err != nil { 151 return nil, err 152 } 153 154 return out, nil 155 } 156 157 func (s *HTTPServer) AgentMonitor(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 158 var secret string 159 s.parseToken(req, &secret) 160 161 // Check agent read permissions 162 if aclObj, err := s.agent.Server().ResolveToken(secret); err != nil { 163 return nil, err 164 } else if aclObj != nil && !aclObj.AllowAgentRead() { 165 return nil, structs.ErrPermissionDenied 166 } 167 168 // Get the provided loglevel. 169 logLevel := req.URL.Query().Get("log_level") 170 if logLevel == "" { 171 logLevel = "INFO" 172 } 173 174 if log.LevelFromString(logLevel) == log.NoLevel { 175 return nil, CodedError(400, fmt.Sprintf("Unknown log level: %s", logLevel)) 176 } 177 178 logJSON := false 179 logJSONStr := req.URL.Query().Get("log_json") 180 if logJSONStr != "" { 181 parsed, err := strconv.ParseBool(logJSONStr) 182 if err != nil { 183 return nil, CodedError(400, fmt.Sprintf("Unknown option for log json: %v", err)) 184 } 185 logJSON = parsed 186 } 187 188 plainText := false 189 plainTextStr := req.URL.Query().Get("plain") 190 if plainTextStr != "" { 191 parsed, err := strconv.ParseBool(plainTextStr) 192 if err != nil { 193 return nil, CodedError(400, fmt.Sprintf("Unknown option for plain: %v", err)) 194 } 195 plainText = parsed 196 } 197 198 nodeID := req.URL.Query().Get("node_id") 199 // Build the request and parse the ACL token 200 args := cstructs.MonitorRequest{ 201 NodeID: nodeID, 202 ServerID: req.URL.Query().Get("server_id"), 203 LogLevel: logLevel, 204 LogJSON: logJSON, 205 PlainText: plainText, 206 } 207 208 // if node and server were requested return error 209 if args.NodeID != "" && args.ServerID != "" { 210 return nil, CodedError(400, "Cannot target node and server simultaneously") 211 } 212 213 s.parse(resp, req, &args.QueryOptions.Region, &args.QueryOptions) 214 215 // Make the RPC 216 var handler structs.StreamingRpcHandler 217 var handlerErr error 218 if nodeID != "" { 219 // Determine the handler to use 220 useLocalClient, useClientRPC, useServerRPC := s.rpcHandlerForNode(nodeID) 221 if useLocalClient { 222 handler, handlerErr = s.agent.Client().StreamingRpcHandler("Agent.Monitor") 223 } else if useClientRPC { 224 handler, handlerErr = s.agent.Client().RemoteStreamingRpcHandler("Agent.Monitor") 225 } else if useServerRPC { 226 handler, handlerErr = s.agent.Server().StreamingRpcHandler("Agent.Monitor") 227 } else { 228 handlerErr = CodedError(400, "No local Node and node_id not provided") 229 } 230 } else { 231 // No node id we want to monitor this server 232 handler, handlerErr = s.agent.Server().StreamingRpcHandler("Agent.Monitor") 233 } 234 235 if handlerErr != nil { 236 return nil, CodedError(500, handlerErr.Error()) 237 } 238 httpPipe, handlerPipe := net.Pipe() 239 decoder := codec.NewDecoder(httpPipe, structs.MsgpackHandle) 240 encoder := codec.NewEncoder(httpPipe, structs.MsgpackHandle) 241 242 ctx, cancel := context.WithCancel(req.Context()) 243 go func() { 244 <-ctx.Done() 245 httpPipe.Close() 246 }() 247 248 // Create an output that gets flushed on every write 249 output := ioutils.NewWriteFlusher(resp) 250 251 // create an error channel to handle errors 252 errCh := make(chan HTTPCodedError, 2) 253 254 // stream response 255 go func() { 256 defer cancel() 257 258 // Send the request 259 if err := encoder.Encode(args); err != nil { 260 errCh <- CodedError(500, err.Error()) 261 return 262 } 263 264 for { 265 select { 266 case <-ctx.Done(): 267 errCh <- nil 268 return 269 default: 270 } 271 272 var res cstructs.StreamErrWrapper 273 if err := decoder.Decode(&res); err != nil { 274 errCh <- CodedError(500, err.Error()) 275 return 276 } 277 decoder.Reset(httpPipe) 278 279 if err := res.Error; err != nil { 280 if err.Code != nil { 281 errCh <- CodedError(int(*err.Code), err.Error()) 282 return 283 } 284 } 285 286 if _, err := io.Copy(output, bytes.NewReader(res.Payload)); err != nil { 287 errCh <- CodedError(500, err.Error()) 288 return 289 } 290 } 291 }() 292 293 handler(handlerPipe) 294 cancel() 295 codedErr := <-errCh 296 297 if codedErr != nil && 298 (codedErr == io.EOF || 299 strings.Contains(codedErr.Error(), "closed") || 300 strings.Contains(codedErr.Error(), "EOF")) { 301 codedErr = nil 302 } 303 return nil, codedErr 304 } 305 306 func (s *HTTPServer) AgentForceLeaveRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 307 if req.Method != "PUT" && req.Method != "POST" { 308 return nil, CodedError(405, ErrInvalidMethod) 309 } 310 srv := s.agent.Server() 311 if srv == nil { 312 return nil, CodedError(501, ErrInvalidMethod) 313 } 314 315 var secret string 316 s.parseToken(req, &secret) 317 318 // Check agent write permissions 319 if aclObj, err := s.agent.Server().ResolveToken(secret); err != nil { 320 return nil, err 321 } else if aclObj != nil && !aclObj.AllowAgentWrite() { 322 return nil, structs.ErrPermissionDenied 323 } 324 325 // Get the node to eject 326 node := req.URL.Query().Get("node") 327 if node == "" { 328 return nil, CodedError(400, "missing node to force leave") 329 } 330 331 // Attempt remove 332 err := srv.RemoveFailedNode(node) 333 return nil, err 334 } 335 336 // AgentServersRequest is used to query the list of servers used by the Nomad 337 // Client for RPCs. This endpoint can also be used to update the list of 338 // servers for a given agent. 339 func (s *HTTPServer) AgentServersRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 340 switch req.Method { 341 case "PUT", "POST": 342 return s.updateServers(resp, req) 343 case "GET": 344 return s.listServers(resp, req) 345 default: 346 return nil, CodedError(405, ErrInvalidMethod) 347 } 348 } 349 350 func (s *HTTPServer) listServers(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 351 client := s.agent.Client() 352 if client == nil { 353 return nil, CodedError(501, ErrInvalidMethod) 354 } 355 356 var secret string 357 s.parseToken(req, &secret) 358 359 // Check agent read permissions 360 if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil { 361 return nil, err 362 } else if aclObj != nil && !aclObj.AllowAgentRead() { 363 return nil, structs.ErrPermissionDenied 364 } 365 366 peers := s.agent.client.GetServers() 367 sort.Strings(peers) 368 return peers, nil 369 } 370 371 func (s *HTTPServer) updateServers(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 372 client := s.agent.Client() 373 if client == nil { 374 return nil, CodedError(501, ErrInvalidMethod) 375 } 376 377 // Get the servers from the request 378 servers := req.URL.Query()["address"] 379 if len(servers) == 0 { 380 return nil, CodedError(400, "missing server address") 381 } 382 383 var secret string 384 s.parseToken(req, &secret) 385 386 // Check agent write permissions 387 if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil { 388 return nil, err 389 } else if aclObj != nil && !aclObj.AllowAgentWrite() { 390 return nil, structs.ErrPermissionDenied 391 } 392 393 // Set the servers list into the client 394 s.agent.logger.Trace("adding servers to the client's primary server list", "servers", servers, "path", "/v1/agent/servers", "method", "PUT") 395 if _, err := client.SetServers(servers); err != nil { 396 s.agent.logger.Error("failed adding servers to client's server list", "servers", servers, "error", err, "path", "/v1/agent/servers", "method", "PUT") 397 //TODO is this the right error to return? 398 return nil, CodedError(400, err.Error()) 399 } 400 return nil, nil 401 } 402 403 // KeyringOperationRequest allows an operator to install/delete/use keys 404 func (s *HTTPServer) KeyringOperationRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 405 srv := s.agent.Server() 406 if srv == nil { 407 return nil, CodedError(501, ErrInvalidMethod) 408 } 409 410 var secret string 411 s.parseToken(req, &secret) 412 413 // Check agent write permissions 414 if aclObj, err := srv.ResolveToken(secret); err != nil { 415 return nil, err 416 } else if aclObj != nil && !aclObj.AllowAgentWrite() { 417 return nil, structs.ErrPermissionDenied 418 } 419 420 kmgr := srv.KeyManager() 421 var sresp *serf.KeyResponse 422 var err error 423 424 // Get the key from the req body 425 var args structs.KeyringRequest 426 427 //Get the op 428 op := strings.TrimPrefix(req.URL.Path, "/v1/agent/keyring/") 429 430 switch op { 431 case "list": 432 sresp, err = kmgr.ListKeys() 433 case "install": 434 if err := decodeBody(req, &args); err != nil { 435 return nil, CodedError(500, err.Error()) 436 } 437 sresp, err = kmgr.InstallKey(args.Key) 438 case "use": 439 if err := decodeBody(req, &args); err != nil { 440 return nil, CodedError(500, err.Error()) 441 } 442 sresp, err = kmgr.UseKey(args.Key) 443 case "remove": 444 if err := decodeBody(req, &args); err != nil { 445 return nil, CodedError(500, err.Error()) 446 } 447 sresp, err = kmgr.RemoveKey(args.Key) 448 default: 449 return nil, CodedError(404, "resource not found") 450 } 451 452 if err != nil { 453 return nil, err 454 } 455 kresp := structs.KeyringResponse{ 456 Messages: sresp.Messages, 457 Keys: sresp.Keys, 458 NumNodes: sresp.NumNodes, 459 } 460 return kresp, nil 461 } 462 463 type agentSelf struct { 464 Config *Config `json:"config"` 465 Member Member `json:"member,omitempty"` 466 Stats map[string]map[string]string `json:"stats"` 467 } 468 469 type joinResult struct { 470 NumJoined int `json:"num_joined"` 471 Error string `json:"error"` 472 } 473 474 func (s *HTTPServer) HealthRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 475 if req.Method != "GET" { 476 return nil, CodedError(405, ErrInvalidMethod) 477 } 478 479 var args structs.GenericRequest 480 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 481 return nil, nil 482 } 483 484 health := healthResponse{} 485 getClient := true 486 getServer := true 487 488 // See if we're checking a specific agent type and default to failing 489 if healthType, ok := req.URL.Query()["type"]; ok { 490 getClient = false 491 getServer = false 492 for _, ht := range healthType { 493 switch ht { 494 case "client": 495 getClient = true 496 health.Client = &healthResponseAgent{ 497 Ok: false, 498 Message: "client not enabled", 499 } 500 case "server": 501 getServer = true 502 health.Server = &healthResponseAgent{ 503 Ok: false, 504 Message: "server not enabled", 505 } 506 } 507 } 508 } 509 510 // If we should check the client and it exists assume it's healthy 511 if client := s.agent.Client(); getClient && client != nil { 512 if len(client.GetServers()) == 0 { 513 health.Client = &healthResponseAgent{ 514 Ok: false, 515 Message: "no known servers", 516 } 517 } else { 518 health.Client = &healthResponseAgent{ 519 Ok: true, 520 Message: "ok", 521 } 522 } 523 } 524 525 // If we should check the server and it exists, see if there's a leader 526 if server := s.agent.Server(); getServer && server != nil { 527 health.Server = &healthResponseAgent{ 528 Ok: true, 529 Message: "ok", 530 } 531 532 leader := "" 533 if err := s.agent.RPC("Status.Leader", &args, &leader); err != nil { 534 health.Server.Ok = false 535 health.Server.Message = err.Error() 536 } else if leader == "" { 537 health.Server.Ok = false 538 health.Server.Message = "no leader" 539 } 540 } 541 542 if health.ok() { 543 return &health, nil 544 } 545 546 jsonResp, err := json.Marshal(&health) 547 if err != nil { 548 return nil, err 549 } 550 return nil, CodedError(500, string(jsonResp)) 551 } 552 553 type healthResponse struct { 554 Client *healthResponseAgent `json:"client,omitempty"` 555 Server *healthResponseAgent `json:"server,omitempty"` 556 } 557 558 // ok returns true as long as neither Client nor Server have Ok=false. 559 func (h healthResponse) ok() bool { 560 if h.Client != nil && !h.Client.Ok { 561 return false 562 } 563 if h.Server != nil && !h.Server.Ok { 564 return false 565 } 566 return true 567 } 568 569 type healthResponseAgent struct { 570 Ok bool `json:"ok"` 571 Message string `json:"message,omitempty"` 572 }