http/node: adding a few node endpoints

This commit is contained in:
Armon Dadgar 2015-09-06 14:18:11 -07:00
parent 479a9255d6
commit bb0ba026bc
3 changed files with 267 additions and 0 deletions

View file

@ -66,6 +66,9 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
s.mux.HandleFunc("/v1/jobs", s.wrap(s.JobsRequest))
s.mux.HandleFunc("/v1/job/", s.wrap(s.JobSpecificRequest))
s.mux.HandleFunc("/v1/nodes", s.wrap(s.NodesRequest))
s.mux.HandleFunc("/v1/node/", s.wrap(s.NodeSpecificRequest))
if enableDebug {
s.mux.HandleFunc("/debug/pprof/", pprof.Index)
s.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)

View file

@ -0,0 +1,107 @@
package agent
import (
"net/http"
"strings"
"github.com/hashicorp/nomad/nomad/structs"
)
func (s *HTTPServer) NodesRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
if req.Method != "GET" {
return nil, CodedError(405, ErrInvalidMethod)
}
// TODO NODE LIST
return nil, nil
}
func (s *HTTPServer) NodeSpecificRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
path := strings.TrimPrefix(req.URL.Path, "/v1/node/")
switch {
case strings.HasSuffix(path, "/evaluate"):
nodeName := strings.TrimSuffix(path, "/evaluate")
return s.nodeForceEvaluate(resp, req, nodeName)
case strings.HasSuffix(path, "/allocations"):
nodeName := strings.TrimSuffix(path, "/allocations")
return s.nodeAllocations(resp, req, nodeName)
case strings.HasSuffix(path, "/drain"):
nodeName := strings.TrimSuffix(path, "/drain")
return s.nodeToggleDrain(resp, req, nodeName)
default:
return s.nodeQuery(resp, req, path)
}
}
func (s *HTTPServer) nodeForceEvaluate(resp http.ResponseWriter, req *http.Request,
nodeID string) (interface{}, error) {
if req.Method != "PUT" && req.Method != "POST" {
return nil, CodedError(405, ErrInvalidMethod)
}
args := structs.NodeEvaluateRequest{
NodeID: nodeID,
}
s.parseRegion(req, &args.Region)
var out structs.NodeUpdateResponse
if err := s.agent.RPC("Client.Evaluate", &args, &out); err != nil {
return nil, err
}
setIndex(resp, out.Index)
return out, nil
}
func (s *HTTPServer) nodeAllocations(resp http.ResponseWriter, req *http.Request,
nodeID string) (interface{}, error) {
if req.Method != "GET" {
return nil, CodedError(405, ErrInvalidMethod)
}
args := structs.NodeSpecificRequest{
NodeID: nodeID,
}
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
return nil, nil
}
var out structs.NodeAllocsResponse
if err := s.agent.RPC("Client.GetAllocs", &args, &out); err != nil {
return nil, err
}
setMeta(resp, &out.QueryMeta)
return out.Allocs, nil
}
func (s *HTTPServer) nodeToggleDrain(resp http.ResponseWriter, req *http.Request,
jobName string) (interface{}, error) {
if req.Method != "PUT" && req.Method != "POST" {
return nil, CodedError(405, ErrInvalidMethod)
}
// TODO
return nil, nil
}
func (s *HTTPServer) nodeQuery(resp http.ResponseWriter, req *http.Request,
nodeID string) (interface{}, error) {
if req.Method != "GET" {
return nil, CodedError(405, ErrInvalidMethod)
}
args := structs.NodeSpecificRequest{
NodeID: nodeID,
}
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
return nil, nil
}
var out structs.SingleNodeResponse
if err := s.agent.RPC("Client.GetNode", &args, &out); err != nil {
return nil, err
}
setMeta(resp, &out.QueryMeta)
if out.Node == nil {
return nil, CodedError(404, "node not found")
}
return out.Node, nil
}

View file

@ -0,0 +1,157 @@
package agent
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
)
func TestHTTP_NodeForceEval(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
node := mock.Node()
args := structs.NodeRegisterRequest{
Node: node,
WriteRequest: structs.WriteRequest{Region: "region1"},
}
var resp structs.NodeUpdateResponse
if err := s.Agent.RPC("Client.Register", &args, &resp); err != nil {
t.Fatalf("err: %v", err)
}
// Directly manipulate the state
state := s.Agent.server.State()
alloc1 := mock.Alloc()
alloc1.NodeID = node.ID
err := state.UpdateAllocations(1000, []*structs.Allocation{alloc1})
if err != nil {
t.Fatalf("err: %v", err)
}
// Make the HTTP request
req, err := http.NewRequest("POST", "/v1/node/"+node.ID+"/evaluate", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
respW := httptest.NewRecorder()
// Make the request
obj, err := s.Server.NodeSpecificRequest(respW, req)
if err != nil {
t.Fatalf("err: %v", err)
}
// Check for the index
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
// Check the response
upd := obj.(structs.NodeUpdateResponse)
if len(upd.EvalIDs) == 0 {
t.Fatalf("bad: %v", upd)
}
})
}
func TestHTTP_NodeAllocations(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
node := mock.Node()
args := structs.NodeRegisterRequest{
Node: node,
WriteRequest: structs.WriteRequest{Region: "region1"},
}
var resp structs.NodeUpdateResponse
if err := s.Agent.RPC("Client.Register", &args, &resp); err != nil {
t.Fatalf("err: %v", err)
}
// Directly manipulate the state
state := s.Agent.server.State()
alloc1 := mock.Alloc()
alloc1.NodeID = node.ID
err := state.UpdateAllocations(1000, []*structs.Allocation{alloc1})
if err != nil {
t.Fatalf("err: %v", err)
}
// Make the HTTP request
req, err := http.NewRequest("GET", "/v1/node/"+node.ID+"/allocations", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
respW := httptest.NewRecorder()
// Make the request
obj, err := s.Server.NodeSpecificRequest(respW, req)
if err != nil {
t.Fatalf("err: %v", err)
}
// Check for the index
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
t.Fatalf("missing last contact")
}
// Check the node
allocs := obj.([]*structs.Allocation)
if len(allocs) != 1 || allocs[0].ID != alloc1.ID {
t.Fatalf("bad: %#v", allocs)
}
})
}
func TestHTTP_NodeQuery(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
node := mock.Node()
args := structs.NodeRegisterRequest{
Node: node,
WriteRequest: structs.WriteRequest{Region: "region1"},
}
var resp structs.NodeUpdateResponse
if err := s.Agent.RPC("Client.Register", &args, &resp); err != nil {
t.Fatalf("err: %v", err)
}
// Make the HTTP request
req, err := http.NewRequest("GET", "/v1/node/"+node.ID, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
respW := httptest.NewRecorder()
// Make the request
obj, err := s.Server.NodeSpecificRequest(respW, req)
if err != nil {
t.Fatalf("err: %v", err)
}
// Check for the index
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
t.Fatalf("missing last contact")
}
// Check the node
n := obj.(*structs.Node)
if n.ID != node.ID {
t.Fatalf("bad: %#v", n)
}
})
}