consul/state: adding node dump methods
This commit is contained in:
parent
0df0b1674e
commit
a511e8a42d
|
@ -611,3 +611,94 @@ func (s *StateStore) parseCheckServiceNodes(
|
||||||
|
|
||||||
return lindex, results, nil
|
return lindex, results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NodeInfo is used to generate a dump of a single node. The dump includes
|
||||||
|
// all services and checks which are registered against the node.
|
||||||
|
func (s *StateStore) NodeInfo(nodeID string) (uint64, structs.NodeDump, error) {
|
||||||
|
tx := s.db.Txn(false)
|
||||||
|
defer tx.Abort()
|
||||||
|
|
||||||
|
// Query the node by the passed node ID
|
||||||
|
nodes, err := tx.Get("nodes", "id", nodeID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("failed node lookup: %s", err)
|
||||||
|
}
|
||||||
|
return s.parseNodes(tx, nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeDump is used to generate a dump of all nodes. This call is expensive
|
||||||
|
// as it has to query every node, service, and check. The response can also
|
||||||
|
// be quite large since there is currently no filtering applied.
|
||||||
|
func (s *StateStore) NodeDump() (uint64, structs.NodeDump, error) {
|
||||||
|
tx := s.db.Txn(false)
|
||||||
|
defer tx.Abort()
|
||||||
|
|
||||||
|
// Fetch all of the registered nodes
|
||||||
|
nodes, err := tx.Get("nodes", "id")
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("failed node lookup: %s", err)
|
||||||
|
}
|
||||||
|
return s.parseNodes(tx, nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseNodes takes an iterator over a set of nodes and returns a struct
|
||||||
|
// containing the nodes along with all of their associated services
|
||||||
|
// and/or health checks.
|
||||||
|
func (s *StateStore) parseNodes(
|
||||||
|
tx *memdb.Txn,
|
||||||
|
iter memdb.ResultIterator) (uint64, structs.NodeDump, error) {
|
||||||
|
|
||||||
|
var results structs.NodeDump
|
||||||
|
var lindex uint64
|
||||||
|
for n := iter.Next(); n != nil; n = iter.Next() {
|
||||||
|
node := n.(*structs.Node)
|
||||||
|
if node.ModifyIndex > lindex {
|
||||||
|
lindex = node.ModifyIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the wrapped node
|
||||||
|
dump := &structs.NodeInfo{
|
||||||
|
Node: node.Node,
|
||||||
|
Address: node.Address,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query the node services
|
||||||
|
services, err := tx.Get("services", "node", node.Node)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("failed services lookup: %s", err)
|
||||||
|
}
|
||||||
|
for service := services.Next(); service != nil; service = services.Next() {
|
||||||
|
svc := service.(*structs.ServiceNode)
|
||||||
|
if svc.ModifyIndex > lindex {
|
||||||
|
lindex = svc.ModifyIndex
|
||||||
|
}
|
||||||
|
ns := &structs.NodeService{
|
||||||
|
ID: svc.ServiceID,
|
||||||
|
Service: svc.ServiceName,
|
||||||
|
Address: svc.ServiceAddress,
|
||||||
|
Port: svc.ServicePort,
|
||||||
|
Tags: svc.ServiceTags,
|
||||||
|
}
|
||||||
|
ns.CreateIndex = svc.CreateIndex
|
||||||
|
ns.ModifyIndex = svc.ModifyIndex
|
||||||
|
dump.Services = append(dump.Services, ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query the node checks
|
||||||
|
checks, err := tx.Get("checks", "node", node.Node)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("failed node lookup: %s", err)
|
||||||
|
}
|
||||||
|
for check := checks.Next(); check != nil; check = checks.Next() {
|
||||||
|
chk := check.(*structs.HealthCheck)
|
||||||
|
if chk.ModifyIndex > lindex {
|
||||||
|
lindex = chk.ModifyIndex
|
||||||
|
}
|
||||||
|
dump.Checks = append(dump.Checks, chk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the result to the slice
|
||||||
|
results = append(results, dump)
|
||||||
|
}
|
||||||
|
return lindex, results, nil
|
||||||
|
}
|
||||||
|
|
|
@ -617,3 +617,161 @@ func TestStateStore_CheckServiceNodes(t *testing.T) {
|
||||||
t.Fatalf("bad index: %d", idx)
|
t.Fatalf("bad index: %d", idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStateStore_NodeInfo_NodeDump(t *testing.T) {
|
||||||
|
s := testStateStore(t)
|
||||||
|
|
||||||
|
// Generating a node dump that matches nothing returns empty
|
||||||
|
idx, dump, err := s.NodeInfo("node1")
|
||||||
|
if idx != 0 || dump != nil || err != nil {
|
||||||
|
t.Fatalf("expected (0, nil, nil), got: (%d, %#v, %#v)", idx, dump, err)
|
||||||
|
}
|
||||||
|
idx, dump, err = s.NodeDump()
|
||||||
|
if idx != 0 || dump != nil || err != nil {
|
||||||
|
t.Fatalf("expected (0, nil, nil), got: (%d, %#v, %#v)", idx, dump, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register some nodes
|
||||||
|
testRegisterNode(t, s, 0, "node1")
|
||||||
|
testRegisterNode(t, s, 1, "node2")
|
||||||
|
|
||||||
|
// Register services against them
|
||||||
|
testRegisterService(t, s, 2, "node1", "service1")
|
||||||
|
testRegisterService(t, s, 3, "node1", "service2")
|
||||||
|
testRegisterService(t, s, 4, "node2", "service1")
|
||||||
|
testRegisterService(t, s, 5, "node2", "service2")
|
||||||
|
|
||||||
|
// Register service-level checks
|
||||||
|
testRegisterCheck(t, s, 6, "node1", "service1", "check1", structs.HealthPassing)
|
||||||
|
testRegisterCheck(t, s, 7, "node2", "service1", "check1", structs.HealthPassing)
|
||||||
|
|
||||||
|
// Register node-level checks
|
||||||
|
testRegisterCheck(t, s, 8, "node1", "", "check2", structs.HealthPassing)
|
||||||
|
testRegisterCheck(t, s, 9, "node2", "", "check2", structs.HealthPassing)
|
||||||
|
|
||||||
|
// Check that our result matches what we expect.
|
||||||
|
expect := structs.NodeDump{
|
||||||
|
&structs.NodeInfo{
|
||||||
|
Node: "node1",
|
||||||
|
Checks: structs.HealthChecks{
|
||||||
|
&structs.HealthCheck{
|
||||||
|
Node: "node1",
|
||||||
|
CheckID: "check1",
|
||||||
|
ServiceID: "service1",
|
||||||
|
ServiceName: "service1",
|
||||||
|
Status: structs.HealthPassing,
|
||||||
|
RaftIndex: structs.RaftIndex{
|
||||||
|
CreateIndex: 6,
|
||||||
|
ModifyIndex: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&structs.HealthCheck{
|
||||||
|
Node: "node1",
|
||||||
|
CheckID: "check2",
|
||||||
|
ServiceID: "",
|
||||||
|
ServiceName: "",
|
||||||
|
Status: structs.HealthPassing,
|
||||||
|
RaftIndex: structs.RaftIndex{
|
||||||
|
CreateIndex: 8,
|
||||||
|
ModifyIndex: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: []*structs.NodeService{
|
||||||
|
&structs.NodeService{
|
||||||
|
ID: "service1",
|
||||||
|
Service: "service1",
|
||||||
|
Address: "1.1.1.1",
|
||||||
|
Port: 1111,
|
||||||
|
RaftIndex: structs.RaftIndex{
|
||||||
|
CreateIndex: 2,
|
||||||
|
ModifyIndex: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&structs.NodeService{
|
||||||
|
ID: "service2",
|
||||||
|
Service: "service2",
|
||||||
|
Address: "1.1.1.1",
|
||||||
|
Port: 1111,
|
||||||
|
RaftIndex: structs.RaftIndex{
|
||||||
|
CreateIndex: 3,
|
||||||
|
ModifyIndex: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&structs.NodeInfo{
|
||||||
|
Node: "node2",
|
||||||
|
Checks: structs.HealthChecks{
|
||||||
|
&structs.HealthCheck{
|
||||||
|
Node: "node2",
|
||||||
|
CheckID: "check1",
|
||||||
|
ServiceID: "service1",
|
||||||
|
ServiceName: "service1",
|
||||||
|
Status: structs.HealthPassing,
|
||||||
|
RaftIndex: structs.RaftIndex{
|
||||||
|
CreateIndex: 7,
|
||||||
|
ModifyIndex: 7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&structs.HealthCheck{
|
||||||
|
Node: "node2",
|
||||||
|
CheckID: "check2",
|
||||||
|
ServiceID: "",
|
||||||
|
ServiceName: "",
|
||||||
|
Status: structs.HealthPassing,
|
||||||
|
RaftIndex: structs.RaftIndex{
|
||||||
|
CreateIndex: 9,
|
||||||
|
ModifyIndex: 9,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: []*structs.NodeService{
|
||||||
|
&structs.NodeService{
|
||||||
|
ID: "service1",
|
||||||
|
Service: "service1",
|
||||||
|
Address: "1.1.1.1",
|
||||||
|
Port: 1111,
|
||||||
|
RaftIndex: structs.RaftIndex{
|
||||||
|
CreateIndex: 4,
|
||||||
|
ModifyIndex: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&structs.NodeService{
|
||||||
|
ID: "service2",
|
||||||
|
Service: "service2",
|
||||||
|
Address: "1.1.1.1",
|
||||||
|
Port: 1111,
|
||||||
|
RaftIndex: structs.RaftIndex{
|
||||||
|
CreateIndex: 5,
|
||||||
|
ModifyIndex: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a dump of just a single node
|
||||||
|
idx, dump, err = s.NodeInfo("node1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if idx != 8 {
|
||||||
|
t.Fatalf("bad index: %d", idx)
|
||||||
|
}
|
||||||
|
if len(dump) != 1 || !reflect.DeepEqual(dump[0], expect[0]) {
|
||||||
|
t.Fatalf("bad: %#v", dump)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a dump of all the nodes
|
||||||
|
idx, dump, err = s.NodeDump()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if idx != 9 {
|
||||||
|
t.Fatalf("bad index: %d", 9)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(dump, expect) {
|
||||||
|
t.Fatalf("bad: %#v", dump[0].Services[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue