From 258e2c153d2327efd8c000f2661471ee30c38e86 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Sat, 17 Oct 2015 16:02:06 +0000 Subject: [PATCH 1/3] Sort the structs returned by /api/topology --- app/api_topologies.go | 12 +++++------ app/router.go | 50 +++++++++++++++++++++++++++++++------------ 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/app/api_topologies.go b/app/api_topologies.go index e5495a772..8b3160e11 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -38,21 +38,21 @@ func makeTopologyList(rep xfer.Reporter) func(w http.ResponseWriter, r *http.Req rpt = rep.Report() topologies = []APITopologyDesc{} ) - topologyRegistry.walk(func(name string, def topologyView, subDefs map[string]topologyView) { + topologyRegistry.walk(func(def topologyViewAndID, subDefs topologyViewAndIDs) { describedSubDefs := []APITopologyDesc{} - for subName, subDef := range subDefs { + for _, subDef := range subDefs { describedSubDefs = append(describedSubDefs, APITopologyDesc{ Name: subDef.human, - URL: "/api/topology/" + subName, - Options: makeTopologyOptions(subDef), + URL: "/api/topology/" + subDef.id, + Options: makeTopologyOptions(subDef.topologyView), Stats: stats(subDef.renderer, rpt), }) } topologies = append(topologies, APITopologyDesc{ Name: def.human, - URL: "/api/topology/" + name, + URL: "/api/topology/" + def.id, SubTopologies: describedSubDefs, - Options: makeTopologyOptions(def), + Options: makeTopologyOptions(def.topologyView), Stats: stats(def.renderer, rpt), }) }) diff --git a/app/router.go b/app/router.go index 93f58a1cd..a5bd928ab 100644 --- a/app/router.go +++ b/app/router.go @@ -5,6 +5,7 @@ import ( "encoding/gob" "net/http" "net/url" + "sort" "strings" "sync" @@ -143,6 +144,32 @@ type registry struct { items map[string]topologyView } +type topologyViewAndID struct { + topologyView + id string +} + +type topologyViewAndIDs []topologyViewAndID + +func (a topologyViewAndIDs) Len() int { return len(a) } +func (a topologyViewAndIDs) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a topologyViewAndIDs) Less(i, j int) bool { return a[i].id < a[j].id } + +func (r *registry) collectTopologyViews(f func(topologyView) bool) topologyViewAndIDs { + result := topologyViewAndIDs{} + for name, def := range r.items { + if !f(def) { + continue + } + result = append(result, topologyViewAndID{ + topologyView: def, + id: name, + }) + } + sort.Sort(result) + return result +} + func (r *registry) add(ts map[string]topologyView) { r.Lock() defer r.Unlock() @@ -163,23 +190,18 @@ func (r *registry) get(name string) (topologyView, bool) { return t, ok } -func (r *registry) walk(f func(string, topologyView, map[string]topologyView)) { +func (r *registry) walk(f func(topologyViewAndID, topologyViewAndIDs)) { r.RLock() defer r.RUnlock() - for name, def := range r.items { - if def.parent != "" { - continue - } + parents := r.collectTopologyViews(func(def topologyView) bool { + return def.parent == "" + }) - subDefs := map[string]topologyView{} - for subName, subDef := range r.items { - if subDef.parent != name { - continue - } - subDefs[subName] = subDef - } - - f(name, def, subDefs) + for _, parent := range parents { + subDefs := r.collectTopologyViews(func(def topologyView) bool { + return def.parent == parent.id + }) + f(parent, subDefs) } } From 305e1f482c9ae535a8805673949dc3805da113f2 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Sat, 17 Oct 2015 17:16:34 +0000 Subject: [PATCH 2/3] Refactor topology registry etc; merge a bunch of redundant types. --- app/api_topologies.go | 242 ++++++++++++++++++++++++++++++++++-------- app/api_topology.go | 10 +- app/router.go | 194 --------------------------------- 3 files changed, 204 insertions(+), 242 deletions(-) diff --git a/app/api_topologies.go b/app/api_topologies.go index 8b3160e11..642d1202e 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -2,19 +2,125 @@ package main import ( "net/http" + "sort" + "sync" + + "github.com/gorilla/mux" "github.com/weaveworks/scope/render" "github.com/weaveworks/scope/report" "github.com/weaveworks/scope/xfer" ) +const apiTopologyURL = "/api/topology/" + +var ( + topologyRegistry = ®istry{ + items: map[string]APITopologyDesc{}, + } + kubernetesTopologies = []APITopologyDesc{ + { + id: "pods", + renderer: render.PodRenderer, + Name: "Pods", + Options: map[string][]APITopologyOption{"system": { + {"show", "System containers shown", false, nop}, + {"hide", "System containers hidden", true, render.FilterSystem}, + }}, + }, + { + id: "pods-by-service", + parent: "pods", + renderer: render.PodServiceRenderer, + Name: "by service", + Options: map[string][]APITopologyOption{"system": { + {"show", "System containers shown", false, nop}, + {"hide", "System containers hidden", true, render.FilterSystem}, + }}, + }, + } +) + +func init() { + // Topology option labels should tell the current state. The first item must + // be the verb to get to that state + topologyRegistry.add( + APITopologyDesc{ + id: "applications", + renderer: render.FilterUnconnected(render.ProcessWithContainerNameRenderer), + Name: "Applications", + Options: map[string][]APITopologyOption{"unconnected": { + // Show the user why there are filtered nodes in this view. + // Don't give them the option to show those nodes. + {"hide", "Unconnected nodes hidden", true, nop}, + }}, + }, + APITopologyDesc{ + id: "applications-by-name", + parent: "applications", + renderer: render.FilterUnconnected(render.ProcessNameRenderer), + Name: "by name", + Options: map[string][]APITopologyOption{"unconnected": { + // Ditto above. + {"hide", "Unconnected nodes hidden", true, nop}, + }}, + }, + APITopologyDesc{ + id: "containers", + renderer: render.ContainerWithImageNameRenderer, + Name: "Containers", + Options: map[string][]APITopologyOption{"system": { + {"show", "System containers shown", false, nop}, + {"hide", "System containers hidden", true, render.FilterSystem}, + }}, + }, + APITopologyDesc{ + id: "containers-by-image", + parent: "containers", + renderer: render.ContainerImageRenderer, + Name: "by image", + Options: map[string][]APITopologyOption{"system": { + {"show", "System containers shown", false, nop}, + {"hide", "System containers hidden", true, render.FilterSystem}, + }}, + }, + APITopologyDesc{ + id: "containers-by-hostname", + parent: "containers", + renderer: render.ContainerHostnameRenderer, + Name: "by hostname", + Options: map[string][]APITopologyOption{"system": { + {"show", "System containers shown", false, nop}, + {"hide", "System containers hidden", true, render.FilterSystem}, + }}, + }, + APITopologyDesc{ + id: "hosts", + renderer: render.HostRenderer, + Name: "Hosts", + Options: map[string][]APITopologyOption{}, + }, + ) +} + +// registry is a threadsafe store of the available topologies +type registry struct { + sync.RWMutex + items map[string]APITopologyDesc +} + // APITopologyDesc is returned in a list by the /api/topology handler. type APITopologyDesc struct { - Name string `json:"name"` - URL string `json:"url"` - SubTopologies []APITopologyDesc `json:"sub_topologies,omitempty"` - Options map[string][]APITopologyOption `json:"options"` - Stats *topologyStats `json:"stats,omitempty"` + id string + parent string + renderer render.Renderer + + Name string `json:"name"` + Options map[string][]APITopologyOption `json:"options"` + + URL string `json:"url"` + SubTopologies []APITopologyDesc `json:"sub_topologies,omitempty"` + Stats *topologyStats `json:"stats,omitempty"` } // APITopologyOption describes a ¶m=value to a given topology. @@ -22,6 +128,8 @@ type APITopologyOption struct { Value string `json:"value"` Display string `json:"display"` Default bool `json:"default,omitempty"` + + decorator func(render.Renderer) render.Renderer } type topologyStats struct { @@ -31,6 +139,52 @@ type topologyStats struct { FilteredNodes int `json:"filtered_nodes"` } +type byName []APITopologyDesc + +func (a byName) Len() int { return len(a) } +func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byName) Less(i, j int) bool { return a[i].Name < a[j].Name } + +func (r *registry) add(ts ...APITopologyDesc) { + r.Lock() + defer r.Unlock() + for _, t := range ts { + t.URL = apiTopologyURL + t.id + + if t.parent != "" { + parent := r.items[t.parent] + parent.SubTopologies = append(r.items[t.parent].SubTopologies, t) + sort.Sort(byName(parent.SubTopologies)) + r.items[t.parent] = parent + } + + r.items[t.id] = t + } +} + +func (r *registry) get(name string) (APITopologyDesc, bool) { + r.RLock() + defer r.RUnlock() + t, ok := r.items[name] + return t, ok +} + +func (r *registry) walk(f func(APITopologyDesc)) { + r.RLock() + defer r.RUnlock() + descs := []APITopologyDesc{} + for _, desc := range r.items { + if desc.parent != "" { + continue + } + descs = append(descs, desc) + } + sort.Sort(byName(descs)) + for _, desc := range descs { + f(desc) + } +} + // makeTopologyList returns a handler that yields an APITopologyList. func makeTopologyList(rep xfer.Reporter) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { @@ -38,64 +192,66 @@ func makeTopologyList(rep xfer.Reporter) func(w http.ResponseWriter, r *http.Req rpt = rep.Report() topologies = []APITopologyDesc{} ) - topologyRegistry.walk(func(def topologyViewAndID, subDefs topologyViewAndIDs) { - describedSubDefs := []APITopologyDesc{} - for _, subDef := range subDefs { - describedSubDefs = append(describedSubDefs, APITopologyDesc{ - Name: subDef.human, - URL: "/api/topology/" + subDef.id, - Options: makeTopologyOptions(subDef.topologyView), - Stats: stats(subDef.renderer, rpt), - }) + topologyRegistry.walk(func(desc APITopologyDesc) { + decorateTopologyForRequest(r, &desc) + decorateWithStats(&desc, rpt) + for i := range desc.SubTopologies { + decorateTopologyForRequest(r, &desc.SubTopologies[i]) + decorateWithStats(&desc.SubTopologies[i], rpt) } - topologies = append(topologies, APITopologyDesc{ - Name: def.human, - URL: "/api/topology/" + def.id, - SubTopologies: describedSubDefs, - Options: makeTopologyOptions(def.topologyView), - Stats: stats(def.renderer, rpt), - }) + topologies = append(topologies, desc) }) - respondWith(w, http.StatusOK, topologies) } } -func makeTopologyOptions(view topologyView) map[string][]APITopologyOption { - options := map[string][]APITopologyOption{} - for param, optionVals := range view.options { - for _, optionVal := range optionVals { - options[param] = append(options[param], APITopologyOption{ - Value: optionVal.value, - Display: optionVal.human, - Default: optionVal.def, - }) - } - } - return options -} - -func stats(renderer render.Renderer, rpt report.Report) *topologyStats { +func decorateWithStats(desc *APITopologyDesc, rpt report.Report) { var ( nodes int realNodes int edges int ) - - for _, n := range renderer.Render(rpt) { + for _, n := range desc.renderer.Render(rpt) { nodes++ if !n.Pseudo { realNodes++ } edges += len(n.Adjacency) } - - renderStats := renderer.Stats(rpt) - - return &topologyStats{ + renderStats := desc.renderer.Stats(rpt) + desc.Stats = &topologyStats{ NodeCount: nodes, NonpseudoNodeCount: realNodes, EdgeCount: edges, FilteredNodes: renderStats.FilteredNodes, } } + +func nop(r render.Renderer) render.Renderer { return r } + +func enableKubernetesTopologies() { + topologyRegistry.add(kubernetesTopologies...) +} + +func decorateTopologyForRequest(r *http.Request, topology *APITopologyDesc) { + for param, opts := range topology.Options { + value := r.FormValue(param) + for _, opt := range opts { + if (value == "" && opt.Default) || (opt.Value != "" && opt.Value == value) { + topology.renderer = opt.decorator(topology.renderer) + } + } + } +} + +func captureTopology(rep xfer.Reporter, f func(xfer.Reporter, APITopologyDesc, http.ResponseWriter, *http.Request)) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + topology, ok := topologyRegistry.get(mux.Vars(r)["topology"]) + if !ok { + http.NotFound(w, r) + return + } + decorateTopologyForRequest(r, &topology) + f(rep, topology, w, r) + } +} diff --git a/app/api_topology.go b/app/api_topology.go index 03f96934e..5e9dad197 100644 --- a/app/api_topology.go +++ b/app/api_topology.go @@ -33,14 +33,14 @@ type APIEdge struct { } // Full topology. -func handleTopology(rep xfer.Reporter, t topologyView, w http.ResponseWriter, r *http.Request) { +func handleTopology(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *http.Request) { respondWith(w, http.StatusOK, APITopology{ Nodes: t.renderer.Render(rep.Report()).Prune(), }) } // Websocket for the full topology. This route overlaps with the next. -func handleWs(rep xfer.Reporter, t topologyView, w http.ResponseWriter, r *http.Request) { +func handleWs(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { respondWith(w, http.StatusInternalServerError, err.Error()) return @@ -57,7 +57,7 @@ func handleWs(rep xfer.Reporter, t topologyView, w http.ResponseWriter, r *http. } // Individual nodes. -func handleNode(rep xfer.Reporter, t topologyView, w http.ResponseWriter, r *http.Request) { +func handleNode(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *http.Request) { var ( vars = mux.Vars(r) nodeID = vars["id"] @@ -72,7 +72,7 @@ func handleNode(rep xfer.Reporter, t topologyView, w http.ResponseWriter, r *htt } // Individual edges. -func handleEdge(rep xfer.Reporter, t topologyView, w http.ResponseWriter, r *http.Request) { +func handleEdge(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *http.Request) { var ( vars = mux.Vars(r) localID = vars["local"] @@ -92,7 +92,7 @@ func handleWebsocket( w http.ResponseWriter, r *http.Request, rep xfer.Reporter, - t topologyView, + t APITopologyDesc, loop time.Duration, ) { conn, err := upgrader.Upgrade(w, r, nil) diff --git a/app/router.go b/app/router.go index a5bd928ab..2388ddf33 100644 --- a/app/router.go +++ b/app/router.go @@ -5,14 +5,11 @@ import ( "encoding/gob" "net/http" "net/url" - "sort" "strings" - "sync" "github.com/PuerkitoBio/ghost/handlers" "github.com/gorilla/mux" - "github.com/weaveworks/scope/render" "github.com/weaveworks/scope/report" "github.com/weaveworks/scope/xfer" ) @@ -105,29 +102,6 @@ func makeReportPostHandler(a xfer.Adder) http.HandlerFunc { } } -func decorateTopologyForRequest(r *http.Request, topology *topologyView) { - for param, opts := range topology.options { - value := r.FormValue(param) - for _, opt := range opts { - if (value == "" && opt.def) || (opt.value != "" && opt.value == value) { - topology.renderer = opt.decorator(topology.renderer) - } - } - } -} - -func captureTopology(rep xfer.Reporter, f func(xfer.Reporter, topologyView, http.ResponseWriter, *http.Request)) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - topology, ok := topologyRegistry.get(mux.Vars(r)["topology"]) - if !ok { - http.NotFound(w, r) - return - } - decorateTopologyForRequest(r, &topology) - f(rep, topology, w, r) - } -} - // APIDetails are some generic details that can be fetched from /api type APIDetails struct { ID string `json:"id"` @@ -137,171 +111,3 @@ type APIDetails struct { func apiHandler(w http.ResponseWriter, r *http.Request) { respondWith(w, http.StatusOK, APIDetails{ID: uniqueID, Version: version}) } - -// registry is a threadsafe store of the available topologies -type registry struct { - sync.RWMutex - items map[string]topologyView -} - -type topologyViewAndID struct { - topologyView - id string -} - -type topologyViewAndIDs []topologyViewAndID - -func (a topologyViewAndIDs) Len() int { return len(a) } -func (a topologyViewAndIDs) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a topologyViewAndIDs) Less(i, j int) bool { return a[i].id < a[j].id } - -func (r *registry) collectTopologyViews(f func(topologyView) bool) topologyViewAndIDs { - result := topologyViewAndIDs{} - for name, def := range r.items { - if !f(def) { - continue - } - result = append(result, topologyViewAndID{ - topologyView: def, - id: name, - }) - } - sort.Sort(result) - return result -} - -func (r *registry) add(ts map[string]topologyView) { - r.Lock() - defer r.Unlock() - result := map[string]topologyView{} - for name, t := range r.items { - result[name] = t - } - for name, t := range ts { - result[name] = t - } - r.items = result -} - -func (r *registry) get(name string) (topologyView, bool) { - r.RLock() - defer r.RUnlock() - t, ok := r.items[name] - return t, ok -} - -func (r *registry) walk(f func(topologyViewAndID, topologyViewAndIDs)) { - r.RLock() - defer r.RUnlock() - parents := r.collectTopologyViews(func(def topologyView) bool { - return def.parent == "" - }) - - for _, parent := range parents { - subDefs := r.collectTopologyViews(func(def topologyView) bool { - return def.parent == parent.id - }) - f(parent, subDefs) - } -} - -// Topology option labels should tell the current state. The first item must -// be the verb to get to that state -var topologyRegistry = ®istry{ - items: map[string]topologyView{ - "applications": { - human: "Applications", - parent: "", - renderer: render.FilterUnconnected(render.ProcessWithContainerNameRenderer), - options: optionParams{"unconnected": { - // Show the user why there are filtered nodes in this view. - // Don't give them the option to show those nodes. - {"hide", "Unconnected nodes hidden", true, nop}, - }}, - }, - "applications-by-name": { - human: "by name", - parent: "applications", - renderer: render.FilterUnconnected(render.ProcessNameRenderer), - options: optionParams{"unconnected": { - // Ditto above. - {"hide", "Unconnected nodes hidden", true, nop}, - }}, - }, - "containers": { - human: "Containers", - parent: "", - renderer: render.ContainerWithImageNameRenderer, - options: optionParams{"system": { - {"show", "System containers shown", false, nop}, - {"hide", "System containers hidden", true, render.FilterSystem}, - }}, - }, - "containers-by-image": { - human: "by image", - parent: "containers", - renderer: render.ContainerImageRenderer, - options: optionParams{"system": { - {"show", "System containers shown", false, nop}, - {"hide", "System containers hidden", true, render.FilterSystem}, - }}, - }, - "containers-by-hostname": { - human: "by hostname", - parent: "containers", - renderer: render.ContainerHostnameRenderer, - options: optionParams{"system": { - {"show", "System containers shown", false, nop}, - {"hide", "System containers hidden", true, render.FilterSystem}, - }}, - }, - "hosts": { - human: "Hosts", - parent: "", - renderer: render.HostRenderer, - }, - }, -} - -type topologyView struct { - human string - parent string - renderer render.Renderer - options optionParams -} - -type optionParams map[string][]optionValue // param: values - -type optionValue struct { - value string // "hide" - human string // "Hide system containers" - def bool - decorator func(render.Renderer) render.Renderer -} - -func nop(r render.Renderer) render.Renderer { return r } - -func enableKubernetesTopologies() { - topologyRegistry.add(kubernetesTopologies) -} - -var kubernetesTopologies = map[string]topologyView{ - "pods": { - human: "Pods", - parent: "", - renderer: render.PodRenderer, - options: optionParams{"system": { - {"show", "System containers shown", false, nop}, - {"hide", "System containers hidden", true, render.FilterSystem}, - }}, - }, - "pods-by-service": { - human: "by service", - parent: "pods", - renderer: render.PodServiceRenderer, - options: optionParams{"system": { - {"show", "System containers shown", false, nop}, - {"hide", "System containers hidden", true, render.FilterSystem}, - }}, - }, -} From c9d0418c9fa330a379e829a4727e082b9213a48e Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Mon, 19 Oct 2015 14:09:53 +0000 Subject: [PATCH 3/3] Review feedback --- app/api_topologies.go | 40 ++++++++++++++++++++-------------------- app/router.go | 19 ++++++++++++------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/app/api_topologies.go b/app/api_topologies.go index 642d1202e..6052f86af 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -123,6 +123,12 @@ type APITopologyDesc struct { Stats *topologyStats `json:"stats,omitempty"` } +type byName []APITopologyDesc + +func (a byName) Len() int { return len(a) } +func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byName) Less(i, j int) bool { return a[i].Name < a[j].Name } + // APITopologyOption describes a ¶m=value to a given topology. type APITopologyOption struct { Value string `json:"value"` @@ -139,12 +145,6 @@ type topologyStats struct { FilteredNodes int `json:"filtered_nodes"` } -type byName []APITopologyDesc - -func (a byName) Len() int { return len(a) } -func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byName) Less(i, j int) bool { return a[i].Name < a[j].Name } - func (r *registry) add(ts ...APITopologyDesc) { r.Lock() defer r.Unlock() @@ -153,7 +153,7 @@ func (r *registry) add(ts ...APITopologyDesc) { if t.parent != "" { parent := r.items[t.parent] - parent.SubTopologies = append(r.items[t.parent].SubTopologies, t) + parent.SubTopologies = append(parent.SubTopologies, t) sort.Sort(byName(parent.SubTopologies)) r.items[t.parent] = parent } @@ -186,17 +186,17 @@ func (r *registry) walk(f func(APITopologyDesc)) { } // makeTopologyList returns a handler that yields an APITopologyList. -func makeTopologyList(rep xfer.Reporter) func(w http.ResponseWriter, r *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { +func (r *registry) makeTopologyList(rep xfer.Reporter) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, req *http.Request) { var ( rpt = rep.Report() topologies = []APITopologyDesc{} ) - topologyRegistry.walk(func(desc APITopologyDesc) { - decorateTopologyForRequest(r, &desc) + r.walk(func(desc APITopologyDesc) { + decorateTopologyForRequest(req, &desc) decorateWithStats(&desc, rpt) for i := range desc.SubTopologies { - decorateTopologyForRequest(r, &desc.SubTopologies[i]) + decorateTopologyForRequest(req, &desc.SubTopologies[i]) decorateWithStats(&desc.SubTopologies[i], rpt) } topologies = append(topologies, desc) @@ -229,8 +229,8 @@ func decorateWithStats(desc *APITopologyDesc, rpt report.Report) { func nop(r render.Renderer) render.Renderer { return r } -func enableKubernetesTopologies() { - topologyRegistry.add(kubernetesTopologies...) +func (r *registry) enableKubernetesTopologies() { + r.add(kubernetesTopologies...) } func decorateTopologyForRequest(r *http.Request, topology *APITopologyDesc) { @@ -244,14 +244,14 @@ func decorateTopologyForRequest(r *http.Request, topology *APITopologyDesc) { } } -func captureTopology(rep xfer.Reporter, f func(xfer.Reporter, APITopologyDesc, http.ResponseWriter, *http.Request)) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - topology, ok := topologyRegistry.get(mux.Vars(r)["topology"]) +func (r *registry) captureTopology(rep xfer.Reporter, f func(xfer.Reporter, APITopologyDesc, http.ResponseWriter, *http.Request)) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + topology, ok := r.get(mux.Vars(req)["topology"]) if !ok { - http.NotFound(w, r) + http.NotFound(w, req) return } - decorateTopologyForRequest(r, &topology) - f(rep, topology, w, r) + decorateTopologyForRequest(req, &topology) + f(rep, topology, w, req) } } diff --git a/app/router.go b/app/router.go index 2388ddf33..340c4b837 100644 --- a/app/router.go +++ b/app/router.go @@ -63,12 +63,17 @@ func Router(c collector) *mux.Router { get := router.Methods("GET").Subrouter() get.HandleFunc("/api", gzipHandler(apiHandler)) - get.HandleFunc("/api/topology", gzipHandler(makeTopologyList(c))) - get.HandleFunc("/api/topology/{topology}", gzipHandler(captureTopology(c, handleTopology))) - get.HandleFunc("/api/topology/{topology}/ws", captureTopology(c, handleWs)) // NB not gzip! - get.MatcherFunc(URLMatcher("/api/topology/{topology}/{id}")).HandlerFunc(gzipHandler(captureTopology(c, handleNode))) - get.MatcherFunc(URLMatcher("/api/topology/{topology}/{local}/{remote}")).HandlerFunc(gzipHandler(captureTopology(c, handleEdge))) - get.MatcherFunc(URLMatcher("/api/origin/host/{id}")).HandlerFunc(gzipHandler(makeOriginHostHandler(c))) + get.HandleFunc("/api/topology", gzipHandler(topologyRegistry.makeTopologyList(c))) + get.HandleFunc("/api/topology/{topology}", + gzipHandler(topologyRegistry.captureTopology(c, handleTopology))) + get.HandleFunc("/api/topology/{topology}/ws", + topologyRegistry.captureTopology(c, handleWs)) // NB not gzip! + get.MatcherFunc(URLMatcher("/api/topology/{topology}/{id}")).HandlerFunc( + gzipHandler(topologyRegistry.captureTopology(c, handleNode))) + get.MatcherFunc(URLMatcher("/api/topology/{topology}/{local}/{remote}")).HandlerFunc( + gzipHandler(topologyRegistry.captureTopology(c, handleEdge))) + get.MatcherFunc(URLMatcher("/api/origin/host/{id}")).HandlerFunc( + gzipHandler(makeOriginHostHandler(c))) get.HandleFunc("/api/report", gzipHandler(makeRawReportHandler(c))) get.PathPrefix("/").Handler(http.FileServer(FS(false))) // everything else is static @@ -96,7 +101,7 @@ func makeReportPostHandler(a xfer.Adder) http.HandlerFunc { } a.Add(rpt) if len(rpt.Pod.Nodes) > 0 { - enableKubernetesTopologies() + topologyRegistry.enableKubernetesTopologies() } w.WriteHeader(http.StatusOK) }