diff --git a/consul/state_store.go b/consul/state_store.go index cbdc490537..7d1ebb6010 100644 --- a/consul/state_store.go +++ b/consul/state_store.go @@ -484,6 +484,65 @@ func parseHealthChecks(res []interface{}, err error) structs.HealthChecks { return results } +// CheckServiceNodes returns the nodes associated with a given service, along +// with any associated check +func (s *StateStore) CheckServiceNodes(service string) structs.CheckServiceNodes { + tx, err := s.tables.StartTxn(true) + if err != nil { + panic(fmt.Errorf("Failed to start txn: %v", err)) + } + defer tx.Abort() + + res, err := s.serviceTable.Get("service", service) + return s.parseCheckServiceNodes(tx, res, err) +} + +// CheckServiceNodes returns the nodes associated with a given service, along +// with any associated checks +func (s *StateStore) CheckServiceTagNodes(service, tag string) structs.CheckServiceNodes { + tx, err := s.tables.StartTxn(true) + if err != nil { + panic(fmt.Errorf("Failed to start txn: %v", err)) + } + defer tx.Abort() + + res, err := s.serviceTable.Get("service", service, tag) + return s.parseCheckServiceNodes(tx, res, err) +} + +// parseCheckServiceNodes parses results CheckServiceNodes and CheckServiceTagNodes +func (s *StateStore) parseCheckServiceNodes(tx *MDBTxn, res []interface{}, err error) structs.CheckServiceNodes { + if err != nil { + panic(fmt.Errorf("Failed to get node services: %v", err)) + } + + nodes := make(structs.CheckServiceNodes, len(res)) + for i, r := range res { + srv := r.(*structs.ServiceNode) + + // Get the node + nodeRes, err := s.nodeTable.GetTxn(tx, "id", srv.Node) + if err != nil || len(nodeRes) != 1 { + panic(fmt.Errorf("Failed to join node: %v", err)) + } + + // Get any associated checks + checks := parseHealthChecks(s.checkTable.GetTxn(tx, "node", srv.Node, srv.ServiceID)) + + // Setup the node + nodes[i].Node = *nodeRes[0].(*structs.Node) + nodes[i].Service = structs.NodeService{ + ID: srv.ServiceID, + Service: srv.ServiceName, + Tag: srv.ServiceTag, + Port: srv.ServicePort, + } + nodes[i].Checks = checks + } + + return nodes +} + // Snapshot is used to create a point in time snapshot func (s *StateStore) Snapshot() (*StateSnapshot, error) { // Begin a new txn on all tables diff --git a/consul/state_store_test.go b/consul/state_store_test.go index fc2279acf1..773c95821d 100644 --- a/consul/state_store_test.go +++ b/consul/state_store_test.go @@ -692,3 +692,64 @@ func TestDeleteNodeCheck(t *testing.T) { t.Fatalf("bad: %v", checks[0]) } } + +func TestCheckServiceNodes(t *testing.T) { + store, err := NewStateStore() + if err != nil { + t.Fatalf("err: %v", err) + } + defer store.Close() + + if err := store.EnsureNode(structs.Node{"foo", "127.0.0.1"}); err != nil { + t.Fatalf("err: %v", err) + } + if err := store.EnsureService("foo", "db1", "db", "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(check); err != nil { + t.Fatalf("err: %v") + } + + nodes := store.CheckServiceNodes("db") + if len(nodes) != 1 { + t.Fatalf("Bad: %v", nodes) + } + + if nodes[0].Node.Node != "foo" { + t.Fatalf("Bad: %v", nodes[0]) + } + if nodes[0].Service.ID != "db1" { + t.Fatalf("Bad: %v", nodes[0]) + } + if len(nodes[0].Checks) != 1 { + t.Fatalf("Bad: %v", nodes[0]) + } + if nodes[0].Checks[0].Status != structs.HealthPassing { + t.Fatalf("Bad: %v", nodes[0]) + } + + nodes = store.CheckServiceTagNodes("db", "master") + if len(nodes) != 1 { + t.Fatalf("Bad: %v", nodes) + } + + if nodes[0].Node.Node != "foo" { + t.Fatalf("Bad: %v", nodes[0]) + } + if nodes[0].Service.ID != "db1" { + t.Fatalf("Bad: %v", nodes[0]) + } + if len(nodes[0].Checks) != 1 { + t.Fatalf("Bad: %v", nodes[0]) + } + if nodes[0].Checks[0].Status != structs.HealthPassing { + t.Fatalf("Bad: %v", nodes[0]) + } +} diff --git a/consul/structs/structs.go b/consul/structs/structs.go index 2c86a187d0..274b007fa3 100644 --- a/consul/structs/structs.go +++ b/consul/structs/structs.go @@ -113,13 +113,14 @@ type HealthCheck struct { } type HealthChecks []*HealthCheck -// NodeServiceStatus is used to provide the node, it's service +// CheckServiceNode is used to provide the node, it's service // definition, as well as a HealthCheck that is associated -type NodeServiceStatus struct { +type CheckServiceNode struct { Node Node Service NodeService - Check HealthCheck + Checks HealthChecks } +type CheckServiceNodes []CheckServiceNode // Decode is used to decode a MsgPack encoded object func Decode(buf []byte, out interface{}) error {