Skip to content

Commit

Permalink
Introduce renderers; allow them to recurse.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Wilkie committed Jun 15, 2015
1 parent d71f107 commit 23bdae3
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 52 deletions.
4 changes: 2 additions & 2 deletions app/api_topologies.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ func makeTopologyList(rep Reporter) func(w http.ResponseWriter, r *http.Request)
subTopologies = append(subTopologies, APITopologyDesc{
Name: subDef.human,
URL: "/api/topology/" + subName,
Stats: stats(render(rpt, subDef.maps)),
Stats: stats(subDef.renderer.Render(rpt)),
})
}
}
topologies = append(topologies, APITopologyDesc{
Name: def.human,
URL: "/api/topology/" + name,
SubTopologies: subTopologies,
Stats: stats(render(rpt, def.maps)),
Stats: stats(def.renderer.Render(rpt)),
})
}
respondWith(w, http.StatusOK, topologies)
Expand Down
21 changes: 4 additions & 17 deletions app/api_topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,10 @@ type APIEdge struct {
Metadata report.AggregateMetadata `json:"metadata"`
}

func render(rpt report.Report, maps []topologyMapper) report.RenderableNodes {
result := report.RenderableNodes{}
for _, m := range maps {
rns := m.selector(rpt).RenderBy(m.mapper, m.pseudo)
result.Merge(rns)
}
return result
}

// Full topology.
func handleTopology(rep Reporter, t topologyView, w http.ResponseWriter, r *http.Request) {
respondWith(w, http.StatusOK, APITopology{
Nodes: render(rep.Report(), t.maps),
Nodes: t.renderer.Render(rep.Report()),
})
}

Expand All @@ -69,7 +60,7 @@ func handleNode(rep Reporter, t topologyView, w http.ResponseWriter, r *http.Req
vars = mux.Vars(r)
nodeID = vars["id"]
rpt = rep.Report()
node, ok = render(rpt, t.maps)[nodeID]
node, ok = t.renderer.Render(rep.Report())[nodeID]
)
if !ok {
http.NotFound(w, r)
Expand All @@ -85,13 +76,9 @@ func handleEdge(rep Reporter, t topologyView, w http.ResponseWriter, r *http.Req
localID = vars["local"]
remoteID = vars["remote"]
rpt = rep.Report()
metadata = report.AggregateMetadata{}
metadata = t.renderer.EdgeMetadata(rpt, localID, remoteID)
)

for _, m := range t.maps {
metadata.Merge(m.selector(rpt).EdgeMetadata(m.mapper, localID, remoteID).Transform())
}

respondWith(w, http.StatusOK, APIEdge{Metadata: metadata})
}

Expand Down Expand Up @@ -128,7 +115,7 @@ func handleWebsocket(
tick = time.Tick(loop)
)
for {
newTopo := render(rep.Report(), t.maps)
newTopo := t.renderer.Render(rep.Report())
diff := report.TopoDiff(previousTopo, newTopo)
previousTopo = newTopo

Expand Down
53 changes: 20 additions & 33 deletions app/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/gorilla/mux"

"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
)

Expand Down Expand Up @@ -47,51 +48,37 @@ func apiHandler(w http.ResponseWriter, r *http.Request) {

var topologyRegistry = map[string]topologyView{
"applications": {
human: "Applications",
parent: "",
maps: []topologyMapper{
{report.SelectEndpoint, report.ProcessPID, report.GenericPseudoNode},
},
human: "Applications",
parent: "",
renderer: render.Map{Selector: report.SelectEndpoint, Mapper: report.ProcessPID, Pseudo: report.GenericPseudoNode},
},
"applications-by-name": {
human: "by name",
parent: "applications",
maps: []topologyMapper{
{report.SelectEndpoint, report.ProcessName, report.GenericGroupedPseudoNode},
},
human: "by name",
parent: "applications",
renderer: render.Map{Selector: report.SelectEndpoint, Mapper: report.ProcessName, Pseudo: report.GenericGroupedPseudoNode},
},
"containers": {
human: "Containers",
parent: "",
maps: []topologyMapper{
{report.SelectEndpoint, report.MapEndpoint2Container, report.InternetOnlyPseudoNode},
{report.SelectContainer, report.MapContainerIdentity, report.InternetOnlyPseudoNode},
},
renderer: render.Reduce([]render.Renderer{
render.Map{Selector: report.SelectEndpoint, Mapper: report.MapEndpoint2Container, Pseudo: report.InternetOnlyPseudoNode},
render.Map{Selector: report.SelectContainer, Mapper: report.MapContainerIdentity, Pseudo: report.InternetOnlyPseudoNode},
}),
},
"containers-by-image": {
human: "by image",
parent: "containers",
maps: []topologyMapper{
{report.SelectEndpoint, report.ProcessContainerImage, report.InternetOnlyPseudoNode},
},
human: "by image",
parent: "containers",
renderer: render.Map{Selector: report.SelectEndpoint, Mapper: report.ProcessContainerImage, Pseudo: report.InternetOnlyPseudoNode},
},
"hosts": {
human: "Hosts",
parent: "",
maps: []topologyMapper{
{report.SelectAddress, report.NetworkHostname, report.GenericPseudoNode},
},
human: "Hosts",
parent: "",
renderer: render.Map{Selector: report.SelectAddress, Mapper: report.NetworkHostname, Pseudo: report.GenericPseudoNode},
},
}

type topologyView struct {
human string
parent string
maps []topologyMapper
}

type topologyMapper struct {
selector report.TopologySelector
mapper report.MapFunc
pseudo report.PseudoFunc
human string
parent string
renderer render.Renderer
}
1 change: 1 addition & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies:
mv scope_ui_build.tar $(dirname "$SCOPE_UI_BUILD");
fi
post:
- go version
- go clean -i net
- go install -tags netgo std
- make deps
Expand Down
51 changes: 51 additions & 0 deletions render/render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package render

import (
"github.com/weaveworks/scope/report"
)

// Renderer is something that can render a report to a set of RenderableNodes
type Renderer interface {
Render(report.Report) report.RenderableNodes
EdgeMetadata(rpt report.Report, localID, remoteID string) report.AggregateMetadata
}

// Reduce renderer is a Renderer which merges together the output of several
// other renderers
type Reduce []Renderer

// Render produces a set of RenderableNodes given a Report
func (r Reduce) Render(rpt report.Report) report.RenderableNodes {
result := report.RenderableNodes{}
for _, renderer := range r {
result.Merge(renderer.Render(rpt))
}
return result
}

// EdgeMetadata produces an AggregateMetadata for a given edge
func (r Reduce) EdgeMetadata(rpt report.Report, localID, remoteID string) report.AggregateMetadata {
metadata := report.AggregateMetadata{}
for _, renderer := range r {
metadata.Merge(renderer.EdgeMetadata(rpt, localID, remoteID))
}
return metadata
}

// Map is a Renderer which produces a set of RendererNodes by using a
// Mapper functions and topology selector.
type Map struct {
Selector report.TopologySelector
Mapper report.MapFunc
Pseudo report.PseudoFunc
}

// Render produces a set of RenderableNodes given a Report
func (m Map) Render(rpt report.Report) report.RenderableNodes {
return m.Selector(rpt).RenderBy(m.Mapper, m.Pseudo)
}

// EdgeMetadata produces an AggregateMetadata for a given edge
func (m Map) EdgeMetadata(rpt report.Report, localID, remoteID string) report.AggregateMetadata {
return m.Selector(rpt).EdgeMetadata(m.Mapper, localID, remoteID).Transform()
}
47 changes: 47 additions & 0 deletions render/render_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package render_test

import (
"reflect"
"testing"

"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
)

type mockRenderer struct {
nodes report.RenderableNodes
amd report.AggregateMetadata
}

func (m mockRenderer) Render(rpt report.Report) report.RenderableNodes { return m.nodes }
func (m mockRenderer) EdgeMetadata(rpt report.Report, localID, remoteID string) report.AggregateMetadata {
return m.amd
}

func TestReduceRender(t *testing.T) {
renderer := render.Reduce{
mockRenderer{nodes: report.RenderableNodes{"foo": {ID: "foo"}}},
mockRenderer{nodes: report.RenderableNodes{"bar": {ID: "bar"}}},
}

want := report.RenderableNodes{"foo": {ID: "foo"}, "bar": {ID: "bar"}}
have := renderer.Render(report.MakeReport())

if !reflect.DeepEqual(want, have) {
t.Errorf("want %+v, have %+v", want, have)
}
}

func TestReduceEdge(t *testing.T) {
renderer := render.Reduce{
mockRenderer{amd: report.AggregateMetadata{"foo": 1}},
mockRenderer{amd: report.AggregateMetadata{"bar": 2}},
}

want := report.AggregateMetadata{"foo": 1, "bar": 2}
have := renderer.EdgeMetadata(report.MakeReport(), "", "")

if !reflect.DeepEqual(want, have) {
t.Errorf("want %+v, have %+v", want, have)
}
}

0 comments on commit 23bdae3

Please sign in to comment.