Merge pull request #121 from tomwilkie/refactor

Refactor topology 'views' and their http handlers
This commit is contained in:
Tom Wilkie
2015-05-27 15:18:35 +01:00
7 changed files with 157 additions and 142 deletions

View File

@@ -34,15 +34,15 @@ func makeTopologyList(rep Reporter) func(w http.ResponseWriter, r *http.Request)
url := "/api/topology/" + name
var groupedURL string
if def.hasGrouped {
groupedURL = url + "grouped"
if def.groupedTopology != "" {
groupedURL = "/api/topology/" + def.groupedTopology
}
a = append(a, APITopologyDesc{
Name: def.human,
URL: url,
GroupedURL: groupedURL,
Stats: stats(def.topologySelecter(rpt).RenderBy(def.MapFunc, def.PseudoFunc, false)),
Stats: stats(def.selector(rpt).RenderBy(def.mapper, def.pseudo)),
})
}
respondWith(w, http.StatusOK, a)

View File

@@ -41,68 +41,57 @@ func selectNetwork(r report.Report) report.Topology {
return r.Network
}
// makeTopologyHandlers make /api/topology/* handlers.
func makeTopologyHandlers(
rep Reporter,
topo topologySelecter,
mapping report.MapFunc,
pseudo report.PseudoFunc,
grouped bool,
get *mux.Router,
base string,
) {
// Full topology.
get.HandleFunc(base, func(w http.ResponseWriter, r *http.Request) {
respondWith(w, http.StatusOK, APITopology{
Nodes: topo(rep.Report()).RenderBy(mapping, pseudo, grouped),
})
// Full topology.
func handleTopology(rep Reporter, t topologyView, w http.ResponseWriter, r *http.Request) {
respondWith(w, http.StatusOK, APITopology{
Nodes: t.selector(rep.Report()).RenderBy(t.mapper, t.pseudo),
})
}
// Websocket for the full topology. This route overlaps with the next.
get.HandleFunc(base+"/ws", func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
respondWith(w, http.StatusInternalServerError, err.Error())
// Websocket for the full topology. This route overlaps with the next.
func handleWs(rep Reporter, t topologyView, w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
respondWith(w, http.StatusInternalServerError, err.Error())
return
}
loop := websocketLoop
if t := r.Form.Get("t"); t != "" {
var err error
if loop, err = time.ParseDuration(t); err != nil {
respondWith(w, http.StatusBadRequest, t)
return
}
loop := websocketLoop
if t := r.Form.Get("t"); t != "" {
var err error
if loop, err = time.ParseDuration(t); err != nil {
respondWith(w, http.StatusBadRequest, t)
return
}
}
handleWebsocket(w, r, rep, topo, mapping, pseudo, grouped, loop)
})
}
handleWebsocket(w, r, rep, t, loop)
}
// Individual nodes.
get.HandleFunc(base+"/{id}", func(w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
nodeID = vars["id"]
rpt = rep.Report()
node, ok = topo(rpt).RenderBy(mapping, pseudo, grouped)[nodeID]
)
if !ok {
http.NotFound(w, r)
return
}
originHostFunc := func(id string) (OriginHost, bool) { return getOriginHost(rpt.HostMetadatas, id) }
originNodeFunc := func(id string) (OriginNode, bool) { return getOriginNode(topo(rpt), id) }
respondWith(w, http.StatusOK, APINode{Node: makeDetailed(node, originHostFunc, originNodeFunc)})
})
// Individual nodes.
func handleNode(rep Reporter, t topologyView, w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
nodeID = vars["id"]
rpt = rep.Report()
node, ok = t.selector(rpt).RenderBy(t.mapper, t.pseudo)[nodeID]
)
if !ok {
http.NotFound(w, r)
return
}
originHostFunc := func(id string) (OriginHost, bool) { return getOriginHost(rpt.HostMetadatas, id) }
originNodeFunc := func(id string) (OriginNode, bool) { return getOriginNode(t.selector(rpt), id) }
respondWith(w, http.StatusOK, APINode{Node: makeDetailed(node, originHostFunc, originNodeFunc)})
}
// Individual edges.
get.HandleFunc(base+"/{local}/{remote}", func(w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
localID = vars["local"]
remoteID = vars["remote"]
rpt = rep.Report()
metadata = topo(rpt).EdgeMetadata(mapping, grouped, localID, remoteID).Transform()
)
respondWith(w, http.StatusOK, APIEdge{Metadata: metadata})
})
// Individual edges.
func handleEdge(rep Reporter, t topologyView, w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
localID = vars["local"]
remoteID = vars["remote"]
rpt = rep.Report()
metadata = t.selector(rpt).EdgeMetadata(t.mapper, localID, remoteID).Transform()
)
respondWith(w, http.StatusOK, APIEdge{Metadata: metadata})
}
var upgrader = websocket.Upgrader{
@@ -113,10 +102,7 @@ func handleWebsocket(
w http.ResponseWriter,
r *http.Request,
rep Reporter,
topo topologySelecter,
mapping report.MapFunc,
psuedo report.PseudoFunc,
grouped bool,
t topologyView,
loop time.Duration,
) {
conn, err := upgrader.Upgrade(w, r, nil)
@@ -141,7 +127,7 @@ func handleWebsocket(
tick = time.Tick(loop)
)
for {
newTopo := topo(rep.Report()).RenderBy(mapping, psuedo, grouped)
newTopo := t.selector(rep.Report()).RenderBy(t.mapper, t.pseudo)
diff := report.TopoDiff(previousTopo, newTopo)
previousTopo = newTopo

View File

@@ -14,42 +14,39 @@ func Router(c Reporter) *mux.Router {
router := mux.NewRouter()
get := router.Methods("GET").Subrouter()
get.HandleFunc("/api/topology", makeTopologyList(c))
for name, def := range topologyRegistry {
makeTopologyHandlers(
c,
def.topologySelecter,
def.MapFunc,
def.PseudoFunc,
false, // not grouped
get,
"/api/topology/"+name,
)
if def.hasGrouped {
makeTopologyHandlers(
c,
def.topologySelecter,
def.MapFunc,
def.PseudoFunc,
true, // grouped
get,
"/api/topology/"+name+"grouped",
)
}
}
get.HandleFunc("/api/topology/{topology}", captureTopology(c, handleTopology))
get.HandleFunc("/api/topology/{topology}/ws", captureTopology(c, handleWs))
get.HandleFunc("/api/topology/{topology}/{id}", captureTopology(c, handleNode))
get.HandleFunc("/api/topology/{topology}/{local}/{remote}", captureTopology(c, handleEdge))
get.HandleFunc("/api/origin/host/{id}", makeOriginHostHandler(c))
get.HandleFunc("/api/report", makeRawReportHandler(c))
get.PathPrefix("/").Handler(http.FileServer(FS(false))) // everything else is static
return router
}
var topologyRegistry = map[string]struct {
human string
topologySelecter
report.MapFunc
report.PseudoFunc
hasGrouped bool
}{
"applications": {"Applications", selectProcess, report.ProcessPID, report.GenericPseudoNode, true},
"containers": {"Containers", selectProcess, report.ProcessContainer, report.NoPseudoNode, true},
"hosts": {"Hosts", selectNetwork, report.NetworkHostname, report.GenericPseudoNode, false},
func captureTopology(rep Reporter, f func(Reporter, topologyView, http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
topology, ok := topologyRegistry[mux.Vars(r)["topology"]]
if !ok {
http.NotFound(w, r)
return
}
f(rep, topology, w, r)
}
}
type topologyView struct {
human string
selector topologySelecter
mapper report.MapFunc
pseudo report.PseudoFunc
groupedTopology string
}
var topologyRegistry = map[string]topologyView{
"applications": {"Applications", selectProcess, report.ProcessPID, report.GenericPseudoNode, "applications-grouped"},
"applications-grouped": {"Applications", selectProcess, report.ProcessName, report.GenericGroupedPseudoNode, ""},
"containers": {"Containers", selectProcess, report.ProcessContainer, report.NoPseudoNode, "containers-grouped"},
"containers-grouped": {"Containers", selectProcess, report.ProcessContainerImage, report.NoPseudoNode, ""},
"hosts": {"Hosts", selectNetwork, report.NetworkHostname, report.GenericPseudoNode, ""},
}