From 2f8448c2865713ea0de83f2517570153e42a3108 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Wed, 8 Jan 2014 15:06:13 -0800 Subject: [PATCH] Adding Health endpoint to combine service nodes with health --- consul/endpoints.md | 1 + consul/health_endpoint.go | 24 ++++++++++ consul/health_endpoint_test.go | 83 ++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/consul/endpoints.md b/consul/endpoints.md index caa0f4b5cf..c109afaeba 100644 --- a/consul/endpoints.md +++ b/consul/endpoints.md @@ -48,4 +48,5 @@ query health information, as well as for nodes to publish changes. * ChecksInState : Gets the checks that in a given state * NodeChecks: Gets the checks a given node has * ServiceChecks: Gets the checks a given service has +* ServiceNodes: Returns the nodes that are part of a service, including health info diff --git a/consul/health_endpoint.go b/consul/health_endpoint.go index b915acc499..8d9f44a938 100644 --- a/consul/health_endpoint.go +++ b/consul/health_endpoint.go @@ -57,3 +57,27 @@ func (h *Health) ServiceChecks(args *structs.ServiceSpecificRequest, *reply = checks return nil } + +// 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 { + if done, err := h.srv.forward("Health.ServiceNodes", args.Datacenter, args, reply); done { + return err + } + + // Verify the arguments + if args.ServiceName == "" { + return fmt.Errorf("Must provide service name") + } + + // Get the nodes + state := h.srv.fsm.State() + var nodes structs.CheckServiceNodes + if args.TagFilter { + nodes = state.CheckServiceTagNodes(args.ServiceName, args.ServiceTag) + } else { + nodes = state.CheckServiceNodes(args.ServiceName) + } + + *reply = nodes + return nil +} diff --git a/consul/health_endpoint_test.go b/consul/health_endpoint_test.go index 7488a5f7fb..8dc2287f47 100644 --- a/consul/health_endpoint_test.go +++ b/consul/health_endpoint_test.go @@ -134,3 +134,86 @@ func TestHealth_ServiceChecks(t *testing.T) { t.Fatalf("Bad: %v", checks) } } + +func TestHealth_ServiceNodes(t *testing.T) { + dir1, s1 := testServer(t) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + client := rpcClient(t, s1) + defer client.Close() + + // Wait for leader + time.Sleep(100 * time.Millisecond) + + arg := structs.RegisterRequest{ + Datacenter: "dc1", + Node: "foo", + Address: "127.0.0.1", + Service: &structs.NodeService{ + ID: "db", + Service: "db", + Tag: "master", + }, + Check: &structs.HealthCheck{ + Name: "db connect", + Status: structs.HealthPassing, + ServiceID: "db", + }, + } + var out struct{} + if err := client.Call("Catalog.Register", &arg, &out); err != nil { + t.Fatalf("err: %v", err) + } + + arg = structs.RegisterRequest{ + Datacenter: "dc1", + Node: "bar", + Address: "127.0.0.2", + Service: &structs.NodeService{ + ID: "db", + Service: "db", + Tag: "slave", + }, + Check: &structs.HealthCheck{ + Name: "db connect", + Status: structs.HealthWarning, + ServiceID: "db", + }, + } + if err := client.Call("Catalog.Register", &arg, &out); err != nil { + t.Fatalf("err: %v", err) + } + + var nodes structs.CheckServiceNodes + req := structs.ServiceSpecificRequest{ + Datacenter: "dc1", + ServiceName: "db", + ServiceTag: "master", + TagFilter: false, + } + if err := client.Call("Health.ServiceNodes", &req, &nodes); err != nil { + t.Fatalf("err: %v", err) + } + + if len(nodes) != 2 { + t.Fatalf("Bad: %v", nodes) + } + if nodes[0].Node.Node != "foo" { + t.Fatalf("Bad: %v", nodes[0]) + } + if nodes[1].Node.Node != "bar" { + t.Fatalf("Bad: %v", nodes[1]) + } + if nodes[0].Service.Tag != "master" { + t.Fatalf("Bad: %v", nodes[0]) + } + if nodes[1].Service.Tag != "slave" { + t.Fatalf("Bad: %v", nodes[1]) + } + if nodes[0].Checks[0].Status != structs.HealthPassing { + t.Fatalf("Bad: %v", nodes[0]) + } + if nodes[1].Checks[0].Status != structs.HealthWarning { + t.Fatalf("Bad: %v", nodes[1]) + } +}