mirror of https://github.com/status-im/consul.git
agent: Adding UI services endpoint
This commit is contained in:
parent
52d9b3638e
commit
416ff8f7d6
|
@ -108,6 +108,7 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
|
|||
// API's are under /internal/ui/ to avoid conflict
|
||||
s.mux.HandleFunc("/v1/internal/ui/nodes", s.wrap(s.UINodes))
|
||||
s.mux.HandleFunc("/v1/internal/ui/node/", s.wrap(s.UINodeInfo))
|
||||
s.mux.HandleFunc("/v1/internal/ui/services", s.wrap(s.UIServices))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,19 @@ package agent
|
|||
import (
|
||||
"github.com/hashicorp/consul/consul/structs"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ServiceSummary is used to summarize a service
|
||||
type ServiceSummary struct {
|
||||
Name string
|
||||
Nodes []string
|
||||
ChecksPassing int
|
||||
ChecksWarning int
|
||||
ChecksCritical int
|
||||
}
|
||||
|
||||
// UINodes is used to list the nodes in a given datacenter. We return a
|
||||
// NodeDump which provides overview information for all the nodes
|
||||
func (s *HTTPServer) UINodes(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
|
@ -86,3 +96,68 @@ START:
|
|||
*dump = out.Dump
|
||||
return nil
|
||||
}
|
||||
|
||||
// UIServices is used to list the services in a given datacenter. We return a
|
||||
// ServiceSummary which provides overview information for the service
|
||||
func (s *HTTPServer) UIServices(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
// Get the datacenter
|
||||
var dc string
|
||||
s.parseDC(req, &dc)
|
||||
|
||||
// Get the full node dump...
|
||||
var dump structs.NodeDump
|
||||
if err := s.getNodeDump(resp, dc, "", &dump); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate the summary
|
||||
return summarizeServices(dump), nil
|
||||
}
|
||||
|
||||
func summarizeServices(dump structs.NodeDump) []*ServiceSummary {
|
||||
// Collect the summary information
|
||||
var services []string
|
||||
summary := make(map[string]*ServiceSummary)
|
||||
getService := func(service string) *ServiceSummary {
|
||||
serv, ok := summary[service]
|
||||
if !ok {
|
||||
serv = &ServiceSummary{Name: service}
|
||||
summary[service] = serv
|
||||
services = append(services, service)
|
||||
}
|
||||
return serv
|
||||
}
|
||||
|
||||
// Aggregate all the node information
|
||||
for _, node := range dump {
|
||||
for _, service := range node.Services {
|
||||
sum := getService(service.Service)
|
||||
sum.Nodes = append(sum.Nodes, node.Node)
|
||||
}
|
||||
for _, check := range node.Checks {
|
||||
if check.ServiceName == "" {
|
||||
continue
|
||||
}
|
||||
sum := getService(check.ServiceName)
|
||||
switch check.Status {
|
||||
case structs.HealthPassing:
|
||||
sum.ChecksPassing++
|
||||
case structs.HealthWarning:
|
||||
sum.ChecksWarning++
|
||||
case structs.HealthCritical:
|
||||
sum.ChecksCritical++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the services in sorted order
|
||||
sort.Strings(services)
|
||||
output := make([]*ServiceSummary, len(summary))
|
||||
for idx, service := range services {
|
||||
// Sort the nodes
|
||||
sum := summary[service]
|
||||
sort.Strings(sum.Nodes)
|
||||
output[idx] = sum
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
@ -109,3 +110,96 @@ func TestUiNodeInfo(t *testing.T) {
|
|||
t.Fatalf("bad: %v", node)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummarizeServices(t *testing.T) {
|
||||
dump := structs.NodeDump{
|
||||
&structs.NodeInfo{
|
||||
Node: "foo",
|
||||
Address: "127.0.0.1",
|
||||
Services: []*structs.NodeService{
|
||||
&structs.NodeService{
|
||||
Service: "api",
|
||||
},
|
||||
&structs.NodeService{
|
||||
Service: "web",
|
||||
},
|
||||
},
|
||||
Checks: []*structs.HealthCheck{
|
||||
&structs.HealthCheck{
|
||||
Status: structs.HealthCritical,
|
||||
ServiceName: "",
|
||||
},
|
||||
&structs.HealthCheck{
|
||||
Status: structs.HealthPassing,
|
||||
ServiceName: "web",
|
||||
},
|
||||
&structs.HealthCheck{
|
||||
Status: structs.HealthWarning,
|
||||
ServiceName: "api",
|
||||
},
|
||||
},
|
||||
},
|
||||
&structs.NodeInfo{
|
||||
Node: "bar",
|
||||
Address: "127.0.0.2",
|
||||
Services: []*structs.NodeService{
|
||||
&structs.NodeService{
|
||||
Service: "web",
|
||||
},
|
||||
},
|
||||
Checks: []*structs.HealthCheck{
|
||||
&structs.HealthCheck{
|
||||
Status: structs.HealthCritical,
|
||||
ServiceName: "web",
|
||||
},
|
||||
},
|
||||
},
|
||||
&structs.NodeInfo{
|
||||
Node: "zip",
|
||||
Address: "127.0.0.3",
|
||||
Services: []*structs.NodeService{
|
||||
&structs.NodeService{
|
||||
Service: "cache",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
summary := summarizeServices(dump)
|
||||
if len(summary) != 3 {
|
||||
t.Fatalf("bad: %v", summary)
|
||||
}
|
||||
|
||||
expectAPI := &ServiceSummary{
|
||||
Name: "api",
|
||||
Nodes: []string{"foo"},
|
||||
ChecksPassing: 0,
|
||||
ChecksWarning: 1,
|
||||
ChecksCritical: 0,
|
||||
}
|
||||
if !reflect.DeepEqual(summary[0], expectAPI) {
|
||||
t.Fatalf("bad: %v", summary[0])
|
||||
}
|
||||
|
||||
expectCache := &ServiceSummary{
|
||||
Name: "cache",
|
||||
Nodes: []string{"zip"},
|
||||
ChecksPassing: 0,
|
||||
ChecksWarning: 0,
|
||||
ChecksCritical: 0,
|
||||
}
|
||||
if !reflect.DeepEqual(summary[1], expectCache) {
|
||||
t.Fatalf("bad: %v", summary[1])
|
||||
}
|
||||
|
||||
expectWeb := &ServiceSummary{
|
||||
Name: "web",
|
||||
Nodes: []string{"bar", "foo"},
|
||||
ChecksPassing: 1,
|
||||
ChecksWarning: 0,
|
||||
ChecksCritical: 1,
|
||||
}
|
||||
if !reflect.DeepEqual(summary[2], expectWeb) {
|
||||
t.Fatalf("bad: %v", summary[2])
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue