consul: Adding methods to dump node info

This commit is contained in:
Armon Dadgar 2014-04-27 13:49:20 -06:00 committed by Jack Pearkes
parent 3fe10ccb57
commit 87eba3da8d
3 changed files with 212 additions and 0 deletions

View File

@ -241,6 +241,8 @@ func (s *StateStore) initialize() error {
"NodeChecks": MDBTables{s.checkTable},
"ServiceChecks": MDBTables{s.checkTable},
"CheckServiceNodes": MDBTables{s.nodeTable, s.serviceTable, s.checkTable},
"NodeInfo": MDBTables{s.nodeTable, s.serviceTable, s.checkTable},
"NodeDump": MDBTables{s.nodeTable, s.serviceTable, s.checkTable},
"KVSGet": MDBTables{s.kvsTable},
"KVSList": MDBTables{s.kvsTable},
}
@ -743,6 +745,99 @@ func (s *StateStore) parseCheckServiceNodes(tx *MDBTxn, res []interface{}, err e
return nodes
}
// NodeInfo is used to generate the full info about a node.
func (s *StateStore) NodeInfo(node string) (uint64, *structs.NodeInfo) {
tables := s.queryTables["NodeInfo"]
tx, err := tables.StartTxn(true)
if err != nil {
panic(fmt.Errorf("Failed to start txn: %v", err))
}
defer tx.Abort()
idx, err := tables.LastIndexTxn(tx)
if err != nil {
panic(fmt.Errorf("Failed to get last index: %v", err))
}
res, err := s.nodeTable.GetTxn(tx, "id", node)
dump := s.parseNodeInfo(tx, res, err)
var n *structs.NodeInfo
if len(dump) > 0 {
n = dump[0]
}
return idx, n
}
// NodeDump is used to generate the NodeInfo for all nodes. This is very expensive,
// and should generally be avoided for programatic access.
func (s *StateStore) NodeDump() (uint64, structs.NodeDump) {
tables := s.queryTables["NodeDump"]
tx, err := tables.StartTxn(true)
if err != nil {
panic(fmt.Errorf("Failed to start txn: %v", err))
}
defer tx.Abort()
idx, err := tables.LastIndexTxn(tx)
if err != nil {
panic(fmt.Errorf("Failed to get last index: %v", err))
}
res, err := s.nodeTable.GetTxn(tx, "id")
return idx, s.parseNodeInfo(tx, res, err)
}
// parseNodeInfo is used to scan over the results of a node
// iteration and generate a NodeDump
func (s *StateStore) parseNodeInfo(tx *MDBTxn, res []interface{}, err error) structs.NodeDump {
dump := make(structs.NodeDump, 0, len(res))
if err != nil {
s.logger.Printf("[ERR] consul.state: Failed to get nodes: %v", err)
return dump
}
for _, r := range res {
// Copy the address and node
node := r.(*structs.Node)
info := &structs.NodeInfo{
Node: node.Node,
Address: node.Address,
}
// Get any services of the node
res, err = s.serviceTable.GetTxn(tx, "id", node.Node)
if err != nil {
s.logger.Printf("[ERR] consul.state: Failed to get node services: %v", err)
}
info.Services = make([]*structs.NodeService, 0, len(res))
for _, r := range res {
service := r.(*structs.ServiceNode)
srv := &structs.NodeService{
ID: service.ServiceID,
Service: service.ServiceName,
Tags: service.ServiceTags,
Port: service.ServicePort,
}
info.Services = append(info.Services, srv)
}
// Get any checks of the node
res, err = s.checkTable.GetTxn(tx, "node", node.Node)
if err != nil {
s.logger.Printf("[ERR] consul.state: Failed to get node checks: %v", err)
}
info.Checks = make([]*structs.HealthCheck, 0, len(res))
for _, r := range res {
chk := r.(*structs.HealthCheck)
info.Checks = append(info.Checks, chk)
}
// Add the node info
dump = append(dump, info)
}
return dump
}
// KVSSet is used to create or update a KV entry
func (s *StateStore) KVSSet(index uint64, d *structs.DirEntry) error {
// Start a new txn

View File

@ -1068,6 +1068,108 @@ func TestSS_Register_Deregister_Query(t *testing.T) {
}
}
func TestNodeInfo(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(1, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "db",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "db1",
}
if err := store.EnsureCheck(3, check); err != nil {
t.Fatalf("err: %v")
}
check = &structs.HealthCheck{
Node: "foo",
CheckID: SerfCheckID,
Name: SerfCheckName,
Status: structs.HealthPassing,
}
if err := store.EnsureCheck(4, check); err != nil {
t.Fatalf("err: %v")
}
idx, info := store.NodeInfo("foo")
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if info == nil {
t.Fatalf("Bad: %v", info)
}
if info.Node != "foo" {
t.Fatalf("Bad: %v", info)
}
if info.Services[0].ID != "db1" {
t.Fatalf("Bad: %v", info)
}
if len(info.Checks) != 2 {
t.Fatalf("Bad: %v", info)
}
if info.Checks[0].CheckID != "db" {
t.Fatalf("Bad: %v", info)
}
if info.Checks[1].CheckID != SerfCheckID {
t.Fatalf("Bad: %v", info)
}
}
func TestNodeDump(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(1, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureNode(3, structs.Node{"baz", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(4, "baz", &structs.NodeService{"db1", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
idx, dump := store.NodeDump()
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(dump) != 2 {
t.Fatalf("Bad: %v", dump)
}
info := dump[0]
if info.Node != "baz" {
t.Fatalf("Bad: %v", info)
}
if info.Services[0].ID != "db1" {
t.Fatalf("Bad: %v", info)
}
info = dump[1]
if info.Node != "foo" {
t.Fatalf("Bad: %v", info)
}
if info.Services[0].ID != "db1" {
t.Fatalf("Bad: %v", info)
}
}
func TestKVSSet_Get(t *testing.T) {
store, err := testStateStore()
if err != nil {

View File

@ -220,6 +220,21 @@ type CheckServiceNode struct {
}
type CheckServiceNodes []CheckServiceNode
// NodeInfo is used to dump all associated information about
// a node. This is currently used for the UI only, as it is
// rather expensive to generate.
type NodeInfo struct {
Node string
Address string
Services []*NodeService
Checks []*HealthCheck
}
// NodeDump is used to dump all the nodes with all their
// associated data. This is currently used for the UI only,
// as it is rather expensive to generate.
type NodeDump []*NodeInfo
type IndexedNodes struct {
Nodes Nodes
QueryMeta