consul: Health endpoints support blocking queries

This commit is contained in:
Armon Dadgar 2014-02-05 13:30:18 -08:00
parent bd1a140476
commit 4cee14f58a
4 changed files with 59 additions and 36 deletions

View File

@ -12,35 +12,41 @@ type Health struct {
// ChecksInState is used to get all the checks in a given state // ChecksInState is used to get all the checks in a given state
func (h *Health) ChecksInState(args *structs.ChecksInStateRequest, func (h *Health) ChecksInState(args *structs.ChecksInStateRequest,
reply *structs.HealthChecks) error { reply *structs.IndexedHealthChecks) error {
if done, err := h.srv.forward("Health.ChecksInState", args.Datacenter, args, reply); done { if done, err := h.srv.forward("Health.ChecksInState", args.Datacenter, args, reply); done {
return err return err
} }
// Get the state specific checks // Get the state specific checks
state := h.srv.fsm.State() state := h.srv.fsm.State()
_, checks := state.ChecksInState(args.State) return h.srv.blockingRPC(&args.BlockingQuery,
*reply = checks state.QueryTables("ChecksInState"),
return nil func() (uint64, error) {
reply.Index, reply.HealthChecks = state.ChecksInState(args.State)
return reply.Index, nil
})
} }
// NodeChecks is used to get all the checks for a node // NodeChecks is used to get all the checks for a node
func (h *Health) NodeChecks(args *structs.NodeSpecificRequest, func (h *Health) NodeChecks(args *structs.NodeSpecificRequest,
reply *structs.HealthChecks) error { reply *structs.IndexedHealthChecks) error {
if done, err := h.srv.forward("Health.NodeChecks", args.Datacenter, args, reply); done { if done, err := h.srv.forward("Health.NodeChecks", args.Datacenter, args, reply); done {
return err return err
} }
// Get the node checks // Get the node checks
state := h.srv.fsm.State() state := h.srv.fsm.State()
_, checks := state.NodeChecks(args.Node) return h.srv.blockingRPC(&args.BlockingQuery,
*reply = checks state.QueryTables("NodeChecks"),
return nil func() (uint64, error) {
reply.Index, reply.HealthChecks = state.NodeChecks(args.Node)
return reply.Index, nil
})
} }
// ServiceChecks is used to get all the checks for a service // ServiceChecks is used to get all the checks for a service
func (h *Health) ServiceChecks(args *structs.ServiceSpecificRequest, func (h *Health) ServiceChecks(args *structs.ServiceSpecificRequest,
reply *structs.HealthChecks) error { reply *structs.IndexedHealthChecks) error {
// Reject if tag filtering is on // Reject if tag filtering is on
if args.TagFilter { if args.TagFilter {
return fmt.Errorf("Tag filtering is not supported") return fmt.Errorf("Tag filtering is not supported")
@ -53,13 +59,16 @@ func (h *Health) ServiceChecks(args *structs.ServiceSpecificRequest,
// Get the service checks // Get the service checks
state := h.srv.fsm.State() state := h.srv.fsm.State()
_, checks := state.ServiceChecks(args.ServiceName) return h.srv.blockingRPC(&args.BlockingQuery,
*reply = checks state.QueryTables("ServiceChecks"),
return nil func() (uint64, error) {
reply.Index, reply.HealthChecks = state.ServiceChecks(args.ServiceName)
return reply.Index, nil
})
} }
// ServiceNodes returns all the nodes registered as part of a service including health info // ServiceNodes returns all the nodes registered as part of a service including health info
func (h *Health) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.CheckServiceNodes) error { func (h *Health) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.IndexedCheckServiceNodes) error {
if done, err := h.srv.forward("Health.ServiceNodes", args.Datacenter, args, reply); done { if done, err := h.srv.forward("Health.ServiceNodes", args.Datacenter, args, reply); done {
return err return err
} }
@ -71,13 +80,14 @@ func (h *Health) ServiceNodes(args *structs.ServiceSpecificRequest, reply *struc
// Get the nodes // Get the nodes
state := h.srv.fsm.State() state := h.srv.fsm.State()
var nodes structs.CheckServiceNodes return h.srv.blockingRPC(&args.BlockingQuery,
state.QueryTables("CheckServiceNodes"),
func() (uint64, error) {
if args.TagFilter { if args.TagFilter {
_, nodes = state.CheckServiceTagNodes(args.ServiceName, args.ServiceTag) reply.Index, reply.Nodes = state.CheckServiceTagNodes(args.ServiceName, args.ServiceTag)
} else { } else {
_, nodes = state.CheckServiceNodes(args.ServiceName) reply.Index, reply.Nodes = state.CheckServiceNodes(args.ServiceName)
} }
return reply.Index, nil
*reply = nodes })
return nil
} }

View File

@ -31,15 +31,16 @@ func TestHealth_ChecksInState(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
var checks structs.HealthChecks var out2 structs.IndexedHealthChecks
inState := structs.ChecksInStateRequest{ inState := structs.ChecksInStateRequest{
Datacenter: "dc1", Datacenter: "dc1",
State: structs.HealthPassing, State: structs.HealthPassing,
} }
if err := client.Call("Health.ChecksInState", &inState, &checks); err != nil { if err := client.Call("Health.ChecksInState", &inState, &out2); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
checks := out2.HealthChecks
if len(checks) != 2 { if len(checks) != 2 {
t.Fatalf("Bad: %v", checks) t.Fatalf("Bad: %v", checks)
} }
@ -77,15 +78,16 @@ func TestHealth_NodeChecks(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
var checks structs.HealthChecks var out2 structs.IndexedHealthChecks
node := structs.NodeSpecificRequest{ node := structs.NodeSpecificRequest{
Datacenter: "dc1", Datacenter: "dc1",
Node: "foo", Node: "foo",
} }
if err := client.Call("Health.NodeChecks", &node, &checks); err != nil { if err := client.Call("Health.NodeChecks", &node, &out2); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
checks := out2.HealthChecks
if len(checks) != 1 { if len(checks) != 1 {
t.Fatalf("Bad: %v", checks) t.Fatalf("Bad: %v", checks)
} }
@ -123,15 +125,16 @@ func TestHealth_ServiceChecks(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
var checks structs.HealthChecks var out2 structs.IndexedHealthChecks
node := structs.ServiceSpecificRequest{ node := structs.ServiceSpecificRequest{
Datacenter: "dc1", Datacenter: "dc1",
ServiceName: "db", ServiceName: "db",
} }
if err := client.Call("Health.ServiceChecks", &node, &checks); err != nil { if err := client.Call("Health.ServiceChecks", &node, &out2); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
checks := out2.HealthChecks
if len(checks) != 1 { if len(checks) != 1 {
t.Fatalf("Bad: %v", checks) t.Fatalf("Bad: %v", checks)
} }
@ -189,17 +192,18 @@ func TestHealth_ServiceNodes(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
var nodes structs.CheckServiceNodes var out2 structs.IndexedCheckServiceNodes
req := structs.ServiceSpecificRequest{ req := structs.ServiceSpecificRequest{
Datacenter: "dc1", Datacenter: "dc1",
ServiceName: "db", ServiceName: "db",
ServiceTag: "master", ServiceTag: "master",
TagFilter: false, TagFilter: false,
} }
if err := client.Call("Health.ServiceNodes", &req, &nodes); err != nil { if err := client.Call("Health.ServiceNodes", &req, &out2); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
nodes := out2.Nodes
if len(nodes) != 2 { if len(nodes) != 2 {
t.Fatalf("Bad: %v", nodes) t.Fatalf("Bad: %v", nodes)
} }

View File

@ -199,6 +199,10 @@ func (s *StateStore) initialize() error {
"Services": MDBTables{s.serviceTable}, "Services": MDBTables{s.serviceTable},
"ServiceNodes": MDBTables{s.nodeTable, s.serviceTable}, "ServiceNodes": MDBTables{s.nodeTable, s.serviceTable},
"NodeServices": MDBTables{s.nodeTable, s.serviceTable}, "NodeServices": MDBTables{s.nodeTable, s.serviceTable},
"ChecksInState": MDBTables{s.checkTable},
"NodeChecks": MDBTables{s.checkTable},
"ServiceChecks": MDBTables{s.checkTable},
"CheckServiceNodes": MDBTables{s.nodeTable, s.serviceTable, s.checkTable},
} }
return nil return nil
} }
@ -598,7 +602,7 @@ func parseHealthChecks(idx uint64, res []interface{}, err error) (uint64, struct
// CheckServiceNodes returns the nodes associated with a given service, along // CheckServiceNodes returns the nodes associated with a given service, along
// with any associated check // with any associated check
func (s *StateStore) CheckServiceNodes(service string) (uint64, structs.CheckServiceNodes) { func (s *StateStore) CheckServiceNodes(service string) (uint64, structs.CheckServiceNodes) {
tables := MDBTables{s.nodeTable, s.serviceTable, s.checkTable} tables := s.queryTables["CheckServiceNodes"]
tx, err := tables.StartTxn(true) tx, err := tables.StartTxn(true)
if err != nil { if err != nil {
panic(fmt.Errorf("Failed to start txn: %v", err)) panic(fmt.Errorf("Failed to start txn: %v", err))
@ -617,7 +621,7 @@ func (s *StateStore) CheckServiceNodes(service string) (uint64, structs.CheckSer
// CheckServiceNodes returns the nodes associated with a given service, along // CheckServiceNodes returns the nodes associated with a given service, along
// with any associated checks // with any associated checks
func (s *StateStore) CheckServiceTagNodes(service, tag string) (uint64, structs.CheckServiceNodes) { func (s *StateStore) CheckServiceTagNodes(service, tag string) (uint64, structs.CheckServiceNodes) {
tables := MDBTables{s.nodeTable, s.serviceTable, s.checkTable} tables := s.queryTables["CheckServiceNodes"]
tx, err := tables.StartTxn(true) tx, err := tables.StartTxn(true)
if err != nil { if err != nil {
panic(fmt.Errorf("Failed to start txn: %v", err)) panic(fmt.Errorf("Failed to start txn: %v", err))

View File

@ -167,6 +167,11 @@ type IndexedHealthChecks struct {
HealthChecks HealthChecks HealthChecks HealthChecks
} }
type IndexedCheckServiceNodes struct {
Index uint64
Nodes CheckServiceNodes
}
// Decode is used to decode a MsgPack encoded object // Decode is used to decode a MsgPack encoded object
func Decode(buf []byte, out interface{}) error { func Decode(buf []byte, out interface{}) error {
var handle codec.MsgpackHandle var handle codec.MsgpackHandle