Skip to content

Commit

Permalink
Merge pull request #1375 from weaveworks/1219-alternative
Browse files Browse the repository at this point in the history
Alternative approach to decorators.
  • Loading branch information
paulbellamy committed Apr 27, 2016
2 parents a426ca2 + f4ffd69 commit ef0ba75
Show file tree
Hide file tree
Showing 24 changed files with 286 additions and 255 deletions.
98 changes: 41 additions & 57 deletions app/api_topologies.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,18 @@ func init() {
Options: containerFilters,
},
APITopologyDesc{
id: "containers-by-image",
parent: "containers",
filteredRenderer: render.ContainerImageRenderer,
Name: "by image",
Options: containerFilters,
id: "containers-by-image",
parent: "containers",
renderer: render.ContainerImageRenderer,
Name: "by image",
Options: containerFilters,
},
APITopologyDesc{
id: "containers-by-hostname",
parent: "containers",
filteredRenderer: render.ContainerHostnameRenderer,
Name: "by DNS name",
Options: containerFilters,
id: "containers-by-hostname",
parent: "containers",
renderer: render.ContainerHostnameRenderer,
Name: "by DNS name",
Options: containerFilters,
},
APITopologyDesc{
id: "hosts",
Expand All @@ -123,20 +123,20 @@ func init() {
Rank: 4,
},
APITopologyDesc{
id: "pods",
filteredRenderer: render.PodRenderer,
Name: "Pods",
Rank: 3,
HideIfEmpty: true,
Options: podFilters,
id: "pods",
renderer: render.PodRenderer,
Name: "Pods",
Rank: 3,
HideIfEmpty: true,
Options: podFilters,
},
APITopologyDesc{
id: "pods-by-service",
parent: "pods",
filteredRenderer: render.PodServiceRenderer,
Name: "by service",
HideIfEmpty: true,
Options: serviceFilters,
id: "pods-by-service",
parent: "pods",
renderer: render.PodServiceRenderer,
Name: "by service",
HideIfEmpty: true,
Options: serviceFilters,
},
)
}
Expand All @@ -149,10 +149,9 @@ type registry struct {

// APITopologyDesc is returned in a list by the /api/topology handler.
type APITopologyDesc struct {
id string
parent string
renderer render.Renderer
filteredRenderer func(render.Decorator) render.Renderer
id string
parent string
renderer render.Renderer

Name string `json:"name"`
Rank int `json:"rank"`
Expand Down Expand Up @@ -248,31 +247,31 @@ func (r *registry) makeTopologyList(rep Reporter) CtxHandlerFunc {
func (r *registry) renderTopologies(rpt report.Report, req *http.Request) []APITopologyDesc {
topologies := []APITopologyDesc{}
r.walk(func(desc APITopologyDesc) {
renderer := renderedForRequest(req, desc)
desc.Stats = decorateWithStats(rpt, renderer)
renderer, decorator := renderedForRequest(req, desc)
desc.Stats = decorateWithStats(rpt, renderer, decorator)
for i := range desc.SubTopologies {
renderer := renderedForRequest(req, desc.SubTopologies[i])
desc.SubTopologies[i].Stats = decorateWithStats(rpt, renderer)
renderer, decorator := renderedForRequest(req, desc.SubTopologies[i])
desc.SubTopologies[i].Stats = decorateWithStats(rpt, renderer, decorator)
}
topologies = append(topologies, desc)
})
return topologies
}

func decorateWithStats(rpt report.Report, renderer render.Renderer) topologyStats {
func decorateWithStats(rpt report.Report, renderer render.Renderer, decorator render.Decorator) topologyStats {
var (
nodes int
realNodes int
edges int
)
for _, n := range renderer.Render(rpt) {
for _, n := range renderer.Render(rpt, decorator) {
nodes++
if n.Topology != render.Pseudo {
realNodes++
}
edges += len(n.Adjacency)
}
renderStats := renderer.Stats(rpt)
renderStats := renderer.Stats(rpt, decorator)
return topologyStats{
NodeCount: nodes,
NonpseudoNodeCount: realNodes,
Expand All @@ -281,7 +280,7 @@ func decorateWithStats(rpt report.Report, renderer render.Renderer) topologyStat
}
}

func renderedForRequest(r *http.Request, topology APITopologyDesc) render.Renderer {
func renderedForRequest(r *http.Request, topology APITopologyDesc) (render.Renderer, render.Decorator) {
var filters []render.Decorator
for _, group := range topology.Options {
value := r.FormValue(group.ID)
Expand All @@ -291,14 +290,14 @@ func renderedForRequest(r *http.Request, topology APITopologyDesc) render.Render
}
}
}
decorate := render.ComposeDecorators(filters...)
if topology.filteredRenderer != nil {
return topology.filteredRenderer(decorate)
}
return decorate(topology.renderer)
return topology.renderer, render.ComposeDecorators(filters...)
}

type reportRenderHandler func(context.Context, Reporter, render.Renderer, http.ResponseWriter, *http.Request)
type reportRenderHandler func(
context.Context,
Reporter, render.Renderer, render.Decorator,
http.ResponseWriter, *http.Request,
)

func (r *registry) captureRenderer(rep Reporter, f reportRenderHandler) CtxHandlerFunc {
return func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
Expand All @@ -307,22 +306,7 @@ func (r *registry) captureRenderer(rep Reporter, f reportRenderHandler) CtxHandl
http.NotFound(w, req)
return
}
renderer := renderedForRequest(req, topology)
f(ctx, rep, renderer, w, req)
}
}

func (r *registry) captureRendererWithoutFilters(rep Reporter, f reportRenderHandler) CtxHandlerFunc {
return func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
topology, ok := r.get(mux.Vars(req)["topology"])
if !ok {
http.NotFound(w, req)
return
}
renderer := topology.renderer
if topology.filteredRenderer != nil {
renderer = topology.filteredRenderer(render.FilterNoop)
}
f(ctx, rep, renderer, w, req)
renderer, decorator := renderedForRequest(req, topology)
f(ctx, rep, renderer, decorator, w, req)
}
}
27 changes: 20 additions & 7 deletions app/api_topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,27 @@ type APINode struct {
}

// Full topology.
func handleTopology(ctx context.Context, rep Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
func handleTopology(
ctx context.Context,
rep Reporter, renderer render.Renderer, decorator render.Decorator,
w http.ResponseWriter, r *http.Request,
) {
report, err := rep.Report(ctx)
if err != nil {
respondWith(w, http.StatusInternalServerError, err.Error())
return
}
respondWith(w, http.StatusOK, APITopology{
Nodes: detailed.Summaries(report, renderer.Render(report)),
Nodes: detailed.Summaries(report, renderer.Render(report, decorator)),
})
}

// Websocket for the full topology. This route overlaps with the next.
func handleWs(ctx context.Context, rep Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
func handleWs(
ctx context.Context,
rep Reporter, renderer render.Renderer, decorator render.Decorator,
w http.ResponseWriter, r *http.Request,
) {
if err := r.ParseForm(); err != nil {
respondWith(w, http.StatusInternalServerError, err.Error())
return
Expand All @@ -53,17 +61,21 @@ func handleWs(ctx context.Context, rep Reporter, renderer render.Renderer, w htt
return
}
}
handleWebsocket(ctx, w, r, rep, renderer, loop)
handleWebsocket(ctx, w, r, rep, renderer, decorator, loop)
}

// Individual nodes.
func handleNode(ctx context.Context, rep Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
func handleNode(
ctx context.Context,
rep Reporter, renderer render.Renderer, _ render.Decorator,
w http.ResponseWriter, r *http.Request,
) {
var (
vars = mux.Vars(r)
topologyID = vars["topology"]
nodeID = vars["id"]
report, err = rep.Report(ctx)
rendered = renderer.Render(report)
rendered = renderer.Render(report, render.FilterNoop)
node, ok = rendered[nodeID]
)
if err != nil {
Expand All @@ -83,6 +95,7 @@ func handleWebsocket(
r *http.Request,
rep Reporter,
renderer render.Renderer,
decorator render.Decorator,
loop time.Duration,
) {
conn, err := xfer.Upgrade(w, r, nil)
Expand Down Expand Up @@ -119,7 +132,7 @@ func handleWebsocket(
log.Errorf("Error generating report: %v", err)
return
}
newTopo := detailed.Summaries(report, renderer.Render(report))
newTopo := detailed.Summaries(report, renderer.Render(report, decorator))
diff := detailed.TopoDiff(previousTopo, newTopo)
previousTopo = newTopo

Expand Down
2 changes: 1 addition & 1 deletion app/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func RegisterTopologyRoutes(router *mux.Router, r Reporter) {
get.HandleFunc("/api/topology/{topology}/ws",
requestContextDecorator(topologyRegistry.captureRenderer(r, handleWs))) // NB not gzip!
get.MatcherFunc(URLMatcher("/api/topology/{topology}/{id}")).HandlerFunc(
gzipHandler(requestContextDecorator(topologyRegistry.captureRendererWithoutFilters(r, handleNode))))
gzipHandler(requestContextDecorator(topologyRegistry.captureRenderer(r, handleNode))))
get.HandleFunc("/api/report",
gzipHandler(requestContextDecorator(makeRawReportHandler(r))))
get.HandleFunc("/api/probes",
Expand Down
4 changes: 2 additions & 2 deletions experimental/graphviz/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ func renderTo(rpt report.Report, topology string) (detailed.NodeSummaries, error
"processes": render.FilterUnconnected(render.ProcessWithContainerNameRenderer),
"processes-by-name": render.FilterUnconnected(render.ProcessNameRenderer),
"containers": render.ContainerWithImageNameRenderer,
"containers-by-image": render.ContainerImageRenderer(render.FilterNoop),
"containers-by-image": render.ContainerImageRenderer,
"hosts": render.HostRenderer,
}[topology]
if !ok {
return detailed.NodeSummaries{}, fmt.Errorf("unknown topology %v", topology)
}
return detailed.Summaries(rpt, renderer.Render(rpt)), nil
return detailed.Summaries(rpt, renderer.Render(rpt, render.FilterNoop)), nil
}
20 changes: 10 additions & 10 deletions render/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,26 @@ func BenchmarkContainerWithImageNameStats(b *testing.B) {
benchmarkStats(b, render.ContainerWithImageNameRenderer)
}
func BenchmarkContainerImageRender(b *testing.B) {
benchmarkRender(b, render.ContainerImageRenderer(render.FilterNoop))
benchmarkRender(b, render.ContainerImageRenderer)
}
func BenchmarkContainerImageStats(b *testing.B) {
benchmarkStats(b, render.ContainerImageRenderer(render.FilterNoop))
benchmarkStats(b, render.ContainerImageRenderer)
}
func BenchmarkContainerHostnameRender(b *testing.B) {
benchmarkRender(b, render.ContainerHostnameRenderer(render.FilterNoop))
benchmarkRender(b, render.ContainerHostnameRenderer)
}
func BenchmarkContainerHostnameStats(b *testing.B) {
benchmarkStats(b, render.ContainerHostnameRenderer(render.FilterNoop))
benchmarkStats(b, render.ContainerHostnameRenderer)
}
func BenchmarkHostRender(b *testing.B) { benchmarkRender(b, render.HostRenderer) }
func BenchmarkHostStats(b *testing.B) { benchmarkStats(b, render.HostRenderer) }
func BenchmarkPodRender(b *testing.B) { benchmarkRender(b, render.PodRenderer(render.FilterNoop)) }
func BenchmarkPodStats(b *testing.B) { benchmarkStats(b, render.PodRenderer(render.FilterNoop)) }
func BenchmarkPodRender(b *testing.B) { benchmarkRender(b, render.PodRenderer) }
func BenchmarkPodStats(b *testing.B) { benchmarkStats(b, render.PodRenderer) }
func BenchmarkPodServiceRender(b *testing.B) {
benchmarkRender(b, render.PodServiceRenderer(render.FilterNoop))
benchmarkRender(b, render.PodServiceRenderer)
}
func BenchmarkPodServiceStats(b *testing.B) {
benchmarkStats(b, render.PodServiceRenderer(render.FilterNoop))
benchmarkStats(b, render.PodServiceRenderer)
}

func benchmarkRender(b *testing.B, r render.Renderer) {
Expand All @@ -74,7 +74,7 @@ func benchmarkRender(b *testing.B, r render.Renderer) {
b.StopTimer()
render.ResetCache()
b.StartTimer()
benchmarkRenderResult = r.Render(report)
benchmarkRenderResult = r.Render(report, render.FilterNoop)
if len(benchmarkRenderResult) == 0 {
b.Errorf("Rendered topology contained no nodes")
}
Expand All @@ -94,7 +94,7 @@ func benchmarkStats(b *testing.B, r render.Renderer) {
b.StopTimer()
render.ResetCache()
b.StartTimer()
benchmarkStatsResult = r.Stats(report)
benchmarkStatsResult = r.Stats(report, render.FilterNoop)
}
}

Expand Down
Loading

0 comments on commit ef0ba75

Please sign in to comment.