mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 13:26:07 +00:00
agent: Adding node UI endpoint
This commit is contained in:
parent
fbce850317
commit
acf67a1630
@ -106,7 +106,8 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
|
|||||||
s.mux.Handle("/ui/", http.StripPrefix("/ui/", http.FileServer(http.Dir(s.uiDir))))
|
s.mux.Handle("/ui/", http.StripPrefix("/ui/", http.FileServer(http.Dir(s.uiDir))))
|
||||||
|
|
||||||
// API's are under /internal/ui/ to avoid conflict
|
// API's are under /internal/ui/ to avoid conflict
|
||||||
s.mux.HandleFunc("/v1/internal/ui/nodes/", s.wrap(s.UINodes))
|
s.mux.HandleFunc("/v1/internal/ui/nodes", s.wrap(s.UINodes))
|
||||||
|
s.mux.HandleFunc("/v1/internal/ui/node/", s.wrap(s.UINodeInfo))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,36 +7,76 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// UINodes is used to list the nodes in a given datacenter. We return a
|
// UINodes is used to list the nodes in a given datacenter. We return a
|
||||||
// UINodeList which provides overview information for all the nodes
|
// NodeDump which provides overview information for all the nodes
|
||||||
func (s *HTTPServer) UINodes(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
func (s *HTTPServer) UINodes(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||||
// Verify we have some DC, or use the default
|
// Get the datacenter
|
||||||
dc := strings.TrimPrefix(req.URL.Path, "/v1/internal/ui/nodes/")
|
var dc string
|
||||||
if dc == "" {
|
s.parseDC(req, &dc)
|
||||||
dc = s.agent.config.Datacenter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to ge ta node dump
|
// Try to ge ta node dump
|
||||||
var dump structs.NodeDump
|
var dump structs.NodeDump
|
||||||
if err := s.getNodeDump(resp, dc, &dump); err != nil {
|
if err := s.getNodeDump(resp, dc, "", &dump); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return dump, nil
|
return dump, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UINodeInfo is used to get info on a single node in a given datacenter. We return a
|
||||||
|
// NodeInfo which provides overview information for the node
|
||||||
|
func (s *HTTPServer) UINodeInfo(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||||
|
// Get the datacenter
|
||||||
|
var dc string
|
||||||
|
s.parseDC(req, &dc)
|
||||||
|
|
||||||
|
// Verify we have some DC, or use the default
|
||||||
|
node := strings.TrimPrefix(req.URL.Path, "/v1/internal/ui/node/")
|
||||||
|
if node == "" {
|
||||||
|
resp.WriteHeader(400)
|
||||||
|
resp.Write([]byte("Missing node name"))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get a node dump
|
||||||
|
var dump structs.NodeDump
|
||||||
|
if err := s.getNodeDump(resp, dc, node, &dump); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return only the first entry
|
||||||
|
if len(dump) > 0 {
|
||||||
|
return dump[0], nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// getNodeDump is used to get a dump of all node data. We make a best effort by
|
// getNodeDump is used to get a dump of all node data. We make a best effort by
|
||||||
// reading stale data in the case of an availability outage.
|
// reading stale data in the case of an availability outage.
|
||||||
func (s *HTTPServer) getNodeDump(resp http.ResponseWriter, dc string, dump *structs.NodeDump) error {
|
func (s *HTTPServer) getNodeDump(resp http.ResponseWriter, dc, node string, dump *structs.NodeDump) error {
|
||||||
args := structs.DCSpecificRequest{Datacenter: dc}
|
var args interface{}
|
||||||
|
var method string
|
||||||
|
var allowStale *bool
|
||||||
|
|
||||||
|
if node == "" {
|
||||||
|
raw := structs.DCSpecificRequest{Datacenter: dc}
|
||||||
|
method = "Internal.NodeDump"
|
||||||
|
allowStale = &raw.AllowStale
|
||||||
|
args = &raw
|
||||||
|
} else {
|
||||||
|
raw := &structs.NodeSpecificRequest{Datacenter: dc, Node: node}
|
||||||
|
method = "Internal.NodeInfo"
|
||||||
|
allowStale = &raw.AllowStale
|
||||||
|
args = &raw
|
||||||
|
}
|
||||||
var out structs.IndexedNodeDump
|
var out structs.IndexedNodeDump
|
||||||
defer setMeta(resp, &out.QueryMeta)
|
defer setMeta(resp, &out.QueryMeta)
|
||||||
|
|
||||||
START:
|
START:
|
||||||
if err := s.agent.RPC("Internal.NodeDump", &args, &out); err != nil {
|
if err := s.agent.RPC(method, args, &out); err != nil {
|
||||||
// Retry the request allowing stale data if no leader. The UI should continue
|
// Retry the request allowing stale data if no leader. The UI should continue
|
||||||
// to function even during an outage
|
// to function even during an outage
|
||||||
if strings.Contains(err.Error(), structs.ErrNoLeader.Error()) && !args.AllowStale {
|
if strings.Contains(err.Error(), structs.ErrNoLeader.Error()) && !*allowStale {
|
||||||
args.AllowStale = true
|
*allowStale = true
|
||||||
goto START
|
goto START
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -2,6 +2,7 @@ package agent
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"github.com/hashicorp/consul/consul/structs"
|
"github.com/hashicorp/consul/consul/structs"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -79,3 +80,32 @@ func TestUiNodes(t *testing.T) {
|
|||||||
t.Fatalf("bad: %v", obj)
|
t.Fatalf("bad: %v", obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUiNodeInfo(t *testing.T) {
|
||||||
|
dir, srv := makeHTTPServer(t)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
defer srv.Shutdown()
|
||||||
|
defer srv.agent.Shutdown()
|
||||||
|
|
||||||
|
// Wait for leader
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET",
|
||||||
|
fmt.Sprintf("/v1/internal/ui/node/%s", srv.agent.config.NodeName), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
obj, err := srv.UINodeInfo(resp, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
assertIndex(t, resp)
|
||||||
|
|
||||||
|
// Should be 1 node for the server
|
||||||
|
node := obj.(*structs.NodeInfo)
|
||||||
|
if node.Node != srv.agent.config.NodeName {
|
||||||
|
t.Fatalf("bad: %v", node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user