diff --git a/app/api_topologies.go b/app/api_topologies.go index 70b9636cb..4684b2231 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -2,17 +2,16 @@ package main import ( "net/http" - "strings" "github.com/weaveworks/scope/report" ) // APITopologyDesc is returned in a list by the /api/topology handler. type APITopologyDesc struct { - Name string `json:"name"` - URL string `json:"url"` - GroupedURL string `json:"grouped_url,omitempty"` - Stats topologyStats `json:"stats"` + Name string `json:"name"` + URL string `json:"url"` + SubTopologies []APITopologyDesc `json:"sub_topologies,omitempty"` + Stats *topologyStats `json:"stats,omitempty"` } type topologyStats struct { @@ -25,31 +24,29 @@ type topologyStats struct { func makeTopologyList(rep Reporter) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { rpt := rep.Report() - - var a []APITopologyDesc + topologies := []APITopologyDesc{} for name, def := range topologyRegistry { - if strings.HasSuffix(name, "grouped") { - continue + subTopologies := []APITopologyDesc{} + for subName, subDef := range topologyRegistry { + if subDef.parent == name { + subTopologies = append(subTopologies, APITopologyDesc{ + Name: subDef.human, + URL: "/api/topology/" + subName, + }) + } } - - url := "/api/topology/" + name - var groupedURL string - if def.groupedTopology != "" { - groupedURL = "/api/topology/" + def.groupedTopology - } - - a = append(a, APITopologyDesc{ - Name: def.human, - URL: url, - GroupedURL: groupedURL, - Stats: stats(render(rpt, def.maps)), + topologies = append(topologies, APITopologyDesc{ + Name: def.human, + URL: "/api/topology/" + name, + SubTopologies: subTopologies, + Stats: stats(render(rpt, def.maps)), }) } - respondWith(w, http.StatusOK, a) + respondWith(w, http.StatusOK, topologies) } } -func stats(r report.RenderableNodes) topologyStats { +func stats(r report.RenderableNodes) *topologyStats { var ( nodes int realNodes int @@ -64,7 +61,7 @@ func stats(r report.RenderableNodes) topologyStats { edges += len(n.Adjacency) } - return topologyStats{ + return &topologyStats{ NodeCount: nodes, NonpseudoNodeCount: realNodes, EdgeCount: edges, diff --git a/app/api_topologies_test.go b/app/api_topologies_test.go index 0c9b67da4..7009c6542 100644 --- a/app/api_topologies_test.go +++ b/app/api_topologies_test.go @@ -11,23 +11,29 @@ func TestAPITopology(t *testing.T) { defer ts.Close() body := getRawJSON(t, ts, "/api/topology") - var topos []APITopologyDesc - if err := json.Unmarshal(body, &topos); err != nil { + + var topologies []APITopologyDesc + if err := json.Unmarshal(body, &topologies); err != nil { t.Fatalf("JSON parse error: %s", err) } - equals(t, 3, len(topos)) - for _, topo := range topos { - is200(t, ts, topo.URL) - if topo.GroupedURL != "" { - is200(t, ts, topo.GroupedURL) + equals(t, 5, len(topologies)) + + for _, topology := range topologies { + is200(t, ts, topology.URL) + + for _, subTopology := range topology.SubTopologies { + is200(t, ts, subTopology.URL) } - if have := topo.Stats.EdgeCount; have <= 0 { + + if have := topology.Stats.EdgeCount; have <= 0 { t.Errorf("EdgeCount isn't positive: %d", have) } - if have := topo.Stats.NodeCount; have <= 0 { + + if have := topology.Stats.NodeCount; have <= 0 { t.Errorf("NodeCount isn't positive: %d", have) } - if have := topo.Stats.NonpseudoNodeCount; have <= 0 { + + if have := topology.Stats.NonpseudoNodeCount; have <= 0 { t.Errorf("NonpseudoNodeCount isn't positive: %d", have) } } diff --git a/app/main.go b/app/main.go index 10b3f4196..a43ed3b8c 100644 --- a/app/main.go +++ b/app/main.go @@ -18,7 +18,7 @@ import ( ) // Set during buildtime. -var version = "unknown" +var version = "dev" func main() { var ( diff --git a/app/router.go b/app/router.go index ff4fa7b56..7bd30f178 100644 --- a/app/router.go +++ b/app/router.go @@ -45,10 +45,49 @@ func apiHandler(w http.ResponseWriter, r *http.Request) { respondWith(w, http.StatusOK, APIDetails{Version: version}) } +var topologyRegistry = map[string]topologyView{ + "applications": { + human: "Applications", + parent: "", + maps: []topologyMapper{ + {report.SelectEndpoint, report.ProcessPID, report.GenericPseudoNode}, + }, + }, + "applications-by-name": { + human: "Applications by name", + parent: "applications", + maps: []topologyMapper{ + {report.SelectEndpoint, report.ProcessName, report.GenericGroupedPseudoNode}, + }, + }, + "containers": { + human: "Containers", + parent: "", + maps: []topologyMapper{ + {report.SelectEndpoint, report.MapEndpoint2Container, report.InternetOnlyPseudoNode}, + {report.SelectContainer, report.MapContainerIdentity, report.InternetOnlyPseudoNode}, + }, + }, + "containers-by-image": { + human: "Containers by image", + parent: "containers", + maps: []topologyMapper{ + {report.SelectEndpoint, report.ProcessContainerImage, report.InternetOnlyPseudoNode}, + }, + }, + "hosts": { + human: "Hosts", + parent: "", + maps: []topologyMapper{ + {report.SelectAddress, report.NetworkHostname, report.GenericPseudoNode}, + }, + }, +} + type topologyView struct { - human string - groupedTopology string - maps []topologyMapper + human string + parent string + maps []topologyMapper } type topologyMapper struct { @@ -56,14 +95,3 @@ type topologyMapper struct { mapper report.MapFunc pseudo report.PseudoFunc } - -var topologyRegistry = map[string]topologyView{ - "applications": {"Applications", "applications-grouped", []topologyMapper{{report.SelectEndpoint, report.ProcessPID, report.GenericPseudoNode}}}, - "applications-grouped": {"Applications", "", []topologyMapper{{report.SelectEndpoint, report.ProcessName, report.GenericGroupedPseudoNode}}}, - "containers": {"Containers", "containers-grouped", []topologyMapper{ - {report.SelectEndpoint, report.MapEndpoint2Container, report.InternetOnlyPseudoNode}, - {report.SelectContainer, report.MapContainerIdentity, report.InternetOnlyPseudoNode}, - }}, - "containers-grouped": {"Containers", "", []topologyMapper{{report.SelectEndpoint, report.ProcessContainerImage, report.InternetOnlyPseudoNode}}}, - "hosts": {"Hosts", "", []topologyMapper{{report.SelectAddress, report.NetworkHostname, report.GenericPseudoNode}}}, -}