Add more Opentracing detail to the app (#3383)

* Pass Go context down to Renderers

This is useful for cancellation or tracing.

* Add tracing spans to app

Also log things like number of nodes in Map, total number of reports.
This commit is contained in:
Bryan Boreham
2018-10-26 08:51:33 +03:00
committed by Satyam Zode
parent 296c6f2ab5
commit 3be8cf71dd
31 changed files with 187 additions and 102 deletions

View File

@@ -1,6 +1,7 @@
package app
import (
"context"
"fmt"
"net/http"
"net/url"
@@ -9,9 +10,8 @@ import (
"sync"
"time"
"context"
"github.com/gorilla/mux"
opentracing "github.com/opentracing/opentracing-go"
log "github.com/sirupsen/logrus"
"github.com/weaveworks/scope/probe/docker"
@@ -479,32 +479,36 @@ func (r *Registry) makeTopologyList(rep Reporter) CtxHandlerFunc {
respondWith(w, http.StatusInternalServerError, err)
return
}
respondWith(w, http.StatusOK, r.renderTopologies(report, req))
respondWith(w, http.StatusOK, r.renderTopologies(ctx, report, req))
}
}
func (r *Registry) renderTopologies(rpt report.Report, req *http.Request) []APITopologyDesc {
func (r *Registry) renderTopologies(ctx context.Context, rpt report.Report, req *http.Request) []APITopologyDesc {
span, ctx := opentracing.StartSpanFromContext(ctx, "app.renderTopologies")
defer span.Finish()
topologies := []APITopologyDesc{}
req.ParseForm()
r.walk(func(desc APITopologyDesc) {
renderer, filter, _ := r.RendererForTopology(desc.id, req.Form, rpt)
desc.Stats = computeStats(rpt, renderer, filter)
desc.Stats = computeStats(ctx, rpt, renderer, filter)
for i, sub := range desc.SubTopologies {
renderer, filter, _ := r.RendererForTopology(sub.id, req.Form, rpt)
desc.SubTopologies[i].Stats = computeStats(rpt, renderer, filter)
desc.SubTopologies[i].Stats = computeStats(ctx, rpt, renderer, filter)
}
topologies = append(topologies, desc)
})
return updateFilters(rpt, topologies)
}
func computeStats(rpt report.Report, renderer render.Renderer, transformer render.Transformer) topologyStats {
func computeStats(ctx context.Context, rpt report.Report, renderer render.Renderer, transformer render.Transformer) topologyStats {
span, ctx := opentracing.StartSpanFromContext(ctx, "app.computeStats")
defer span.Finish()
var (
nodes int
realNodes int
edges int
)
r := render.Render(rpt, renderer, transformer)
r := render.Render(ctx, rpt, renderer, transformer)
for _, n := range r.Nodes {
nodes++
if n.Topology != render.Pseudo {

View File

@@ -2,6 +2,7 @@ package app_test
import (
"bytes"
"context"
"net/http/httptest"
"net/url"
"testing"
@@ -118,7 +119,7 @@ func TestRendererForTopologyWithFiltering(t *testing.T) {
input.Container.Nodes[fixture.ClientContainerNodeID] = input.Container.Nodes[fixture.ClientContainerNodeID].WithLatests(map[string]string{
docker.LabelPrefix + "works.weave.role": "system",
})
have := utils.Prune(render.Render(input, renderer, filter).Nodes)
have := utils.Prune(render.Render(context.Background(), input, renderer, filter).Nodes)
want := utils.Prune(expected.RenderedContainers.Copy())
delete(want, fixture.ClientContainerNodeID)
delete(want, render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostID))
@@ -149,7 +150,7 @@ func TestRendererForTopologyNoFiltering(t *testing.T) {
input.Container.Nodes[fixture.ClientContainerNodeID] = input.Container.Nodes[fixture.ClientContainerNodeID].WithLatests(map[string]string{
docker.LabelPrefix + "works.weave.role": "system",
})
have := utils.Prune(render.Render(input, renderer, filter).Nodes)
have := utils.Prune(render.Render(context.Background(), input, renderer, filter).Nodes)
want := utils.Prune(expected.RenderedContainers.Copy())
delete(want, render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostID))
delete(want, render.OutgoingInternetID)
@@ -183,7 +184,8 @@ func getTestContainerLabelFilterTopologySummary(t *testing.T, exclude bool) (det
return nil, err
}
return detailed.Summaries(detailed.RenderContext{Report: fixture.Report}, render.Render(fixture.Report, renderer, filter).Nodes), nil
ctx := context.Background()
return detailed.Summaries(ctx, detailed.RenderContext{Report: fixture.Report}, render.Render(ctx, fixture.Report, renderer, filter).Nodes), nil
}
func TestAPITopologyAddsKubernetes(t *testing.T) {

View File

@@ -42,7 +42,7 @@ type rendererHandler func(context.Context, render.Renderer, render.Transformer,
// Full topology.
func handleTopology(ctx context.Context, renderer render.Renderer, transformer render.Transformer, rc detailed.RenderContext, w http.ResponseWriter, r *http.Request) {
respondWith(w, http.StatusOK, APITopology{
Nodes: detailed.Summaries(rc, render.Render(rc.Report, renderer, transformer).Nodes),
Nodes: detailed.Summaries(ctx, rc, render.Render(ctx, rc.Report, renderer, transformer).Nodes),
})
}
@@ -58,7 +58,7 @@ func handleNode(ctx context.Context, renderer render.Renderer, transformer rende
// filtering, which gives us the node (if it exists at all), and
// then (2) applying the filter separately to that result. If the
// node is lost in the second step, we simply put it back.
nodes := renderer.Render(rc.Report)
nodes := renderer.Render(ctx, rc.Report)
node, ok := nodes.Nodes[nodeID]
if !ok {
http.NotFound(w, r)
@@ -145,7 +145,7 @@ func handleWebsocket(
log.Errorf("Error generating report: %v", err)
return
}
newTopo := detailed.Summaries(RenderContextForReporter(rep, re), render.Render(re, renderer, filter).Nodes)
newTopo := detailed.Summaries(ctx, RenderContextForReporter(rep, re), render.Render(ctx, re, renderer, filter).Nodes)
diff := detailed.TopoDiff(previousTopo, newTopo)
previousTopo = newTopo

View File

@@ -1,6 +1,7 @@
package app
import (
"context"
"flag"
"math/rand"
"net/http"
@@ -100,7 +101,7 @@ func renderForTopology(b *testing.B, topologyID string, report report.Report) re
if err != nil {
b.Fatal(err)
}
return render.Render(report, renderer, filter).Nodes
return render.Render(context.Background(), report, renderer, filter).Nodes
}
func benchmarkRenderTopology(b *testing.B, topologyID string) {
@@ -111,7 +112,7 @@ func benchmarkRenderTopology(b *testing.B, topologyID string) {
func BenchmarkRenderList(b *testing.B) {
benchmarkRender(b, func(report report.Report) {
topologyRegistry.renderTopologies(report, &http.Request{Form: url.Values{}})
topologyRegistry.renderTopologies(context.Background(), report, &http.Request{Form: url.Values{}})
})
}
@@ -140,12 +141,13 @@ func BenchmarkRenderProcessNames(b *testing.B) {
}
func benchmarkSummarizeTopology(b *testing.B, topologyID string) {
ctx := context.Background()
r := getReport(b)
rc := detailed.RenderContext{Report: r}
nodes := renderForTopology(b, topologyID, r)
b.ResetTimer()
for i := 0; i < b.N; i++ {
detailed.Summaries(rc, nodes)
detailed.Summaries(ctx, rc, nodes)
}
}

View File

@@ -15,6 +15,8 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/bluele/gcache"
"github.com/nats-io/nats"
opentracing "github.com/opentracing/opentracing-go"
otlog "github.com/opentracing/opentracing-go/log"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
@@ -336,10 +338,13 @@ func (c *awsCollector) getReports(ctx context.Context, reportKeys []string) ([]r
}
func (c *awsCollector) Report(ctx context.Context, timestamp time.Time) (report.Report, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "awsCollector.Report")
defer span.Finish()
reportKeys, err := c.getReportKeys(ctx, timestamp)
if err != nil {
return report.MakeReport(), err
}
span.LogFields(otlog.Int("keys", len(reportKeys)), otlog.String("timestamp", timestamp.String()))
log.Debugf("Fetching %d reports to %v", len(reportKeys), timestamp)
reports, err := c.getReports(ctx, reportKeys)
if err != nil {

View File

@@ -9,6 +9,8 @@ import (
"context"
"github.com/bradfitz/gomemcache/memcache"
opentracing "github.com/opentracing/opentracing-go"
otlog "github.com/opentracing/opentracing-go/log"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
@@ -150,6 +152,8 @@ func memcacheStatusCode(err error) string {
// FetchReports gets reports from memcache.
func (c *MemcacheClient) FetchReports(ctx context.Context, keys []string) (map[string]report.Report, []string, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "Memcache.FetchReports")
defer span.Finish()
defer memcacheRequests.Add(float64(len(keys)))
var found map[string]*memcache.Item
err := instrument.TimeRequestHistogramStatus(ctx, "Memcache.GetMulti", memcacheRequestDuration, memcacheStatusCode, func(_ context.Context) error {
@@ -157,6 +161,7 @@ func (c *MemcacheClient) FetchReports(ctx context.Context, keys []string) (map[s
found, err = c.client.GetMulti(keys)
return err
})
span.LogFields(otlog.Int("keys", len(keys)), otlog.Int("hits", len(found)))
if err != nil {
return nil, keys, err
}

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"flag"
"io/ioutil"
"testing"
@@ -51,7 +52,7 @@ func benchmarkRender(b *testing.B, r render.Renderer) {
b.StopTimer()
render.ResetCache()
b.StartTimer()
benchmarkRenderResult = r.Render(report)
benchmarkRenderResult = r.Render(context.Background(), report)
if len(benchmarkRenderResult.Nodes) == 0 {
b.Errorf("Rendered topology contained no nodes")
}

View File

@@ -1,6 +1,7 @@
package render
import (
"context"
"regexp"
"github.com/weaveworks/scope/probe/docker"
@@ -53,8 +54,8 @@ type connectionJoin struct {
topology string
}
func (c connectionJoin) Render(rpt report.Report) Nodes {
inputNodes := TopologySelector(c.topology).Render(rpt).Nodes
func (c connectionJoin) Render(ctx context.Context, rpt report.Report) Nodes {
inputNodes := TopologySelector(c.topology).Render(ctx, rpt).Nodes
// Collect all the IPs we are trying to map to, and which ID they map from
var ipNodes = map[string]string{}
for _, n := range inputNodes {
@@ -92,7 +93,7 @@ func (c connectionJoin) Render(rpt report.Report) Nodes {
// from ipNodes, which is populated from c.topology, which
// is where MapEndpoints will look.
return id
}, c.topology).Render(rpt)
}, c.topology).Render(ctx, rpt)
}
// FilterEmpty is a Renderer which filters out nodes which have no children
@@ -121,9 +122,9 @@ type containerWithImageNameRenderer struct {
// Render produces a container graph where the the latest metadata contains the
// container image name, if found.
func (r containerWithImageNameRenderer) Render(rpt report.Report) Nodes {
containers := r.Renderer.Render(rpt)
images := SelectContainerImage.Render(rpt)
func (r containerWithImageNameRenderer) Render(ctx context.Context, rpt report.Report) Nodes {
containers := r.Renderer.Render(ctx, rpt)
images := SelectContainerImage.Render(ctx, rpt)
outputs := make(report.Nodes, len(containers.Nodes))
for id, c := range containers.Nodes {

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"fmt"
"testing"
@@ -53,7 +54,7 @@ func testMap(t *testing.T, f render.MapFunc, input testcase) {
}
func TestContainerRenderer(t *testing.T) {
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(fixture.Report).Nodes)
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(context.Background(), fixture.Report).Nodes)
want := utils.Prune(expected.RenderedContainers)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -67,7 +68,7 @@ func TestContainerFilterRenderer(t *testing.T) {
input.Container.Nodes[fixture.ClientContainerNodeID] = input.Container.Nodes[fixture.ClientContainerNodeID].WithLatests(map[string]string{
docker.LabelPrefix + "works.weave.role": "system",
})
have := utils.Prune(render.Render(input, render.ContainerWithImageNameRenderer, filterApplication).Nodes)
have := utils.Prune(render.Render(context.Background(), input, render.ContainerWithImageNameRenderer, filterApplication).Nodes)
want := utils.Prune(expected.RenderedContainers.Copy())
delete(want, fixture.ClientContainerNodeID)
if !reflect.DeepEqual(want, have) {
@@ -76,7 +77,7 @@ func TestContainerFilterRenderer(t *testing.T) {
}
func TestContainerHostnameRenderer(t *testing.T) {
have := utils.Prune(render.Render(fixture.Report, render.ContainerHostnameRenderer, render.Transformers(nil)).Nodes)
have := utils.Prune(render.Render(context.Background(), fixture.Report, render.ContainerHostnameRenderer, render.Transformers(nil)).Nodes)
want := utils.Prune(expected.RenderedContainerHostnames)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -84,7 +85,7 @@ func TestContainerHostnameRenderer(t *testing.T) {
}
func TestContainerHostnameFilterRenderer(t *testing.T) {
have := utils.Prune(render.Render(fixture.Report, render.ContainerHostnameRenderer, filterSystem).Nodes)
have := utils.Prune(render.Render(context.Background(), fixture.Report, render.ContainerHostnameRenderer, filterSystem).Nodes)
want := utils.Prune(expected.RenderedContainerHostnames.Copy())
delete(want, fixture.ClientContainerHostname)
delete(want, fixture.ServerContainerHostname)
@@ -95,7 +96,7 @@ func TestContainerHostnameFilterRenderer(t *testing.T) {
}
func TestContainerImageRenderer(t *testing.T) {
have := utils.Prune(render.Render(fixture.Report, render.ContainerImageRenderer, render.Transformers(nil)).Nodes)
have := utils.Prune(render.Render(context.Background(), fixture.Report, render.ContainerImageRenderer, render.Transformers(nil)).Nodes)
want := utils.Prune(expected.RenderedContainerImages)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -103,7 +104,7 @@ func TestContainerImageRenderer(t *testing.T) {
}
func TestContainerImageFilterRenderer(t *testing.T) {
have := utils.Prune(render.Render(fixture.Report, render.ContainerImageRenderer, filterSystem).Nodes)
have := utils.Prune(render.Render(context.Background(), fixture.Report, render.ContainerImageRenderer, filterSystem).Nodes)
want := utils.Prune(expected.RenderedContainerHostnames.Copy())
delete(want, fixture.ClientContainerHostname)
delete(want, fixture.ServerContainerHostname)

View File

@@ -1,6 +1,7 @@
package detailed_test
import (
"context"
"fmt"
"testing"
@@ -18,7 +19,7 @@ import (
)
func child(t *testing.T, r render.Renderer, id string) detailed.NodeSummary {
s, ok := detailed.MakeNodeSummary(detailed.RenderContext{Report: fixture.Report}, r.Render(fixture.Report).Nodes[id])
s, ok := detailed.MakeNodeSummary(detailed.RenderContext{Report: fixture.Report}, r.Render(context.Background(), fixture.Report).Nodes[id])
if !ok {
t.Fatalf("Expected node %s to be summarizable, but wasn't", id)
}
@@ -30,7 +31,7 @@ func connectionID(nodeID string, addr string) string {
}
func TestMakeDetailedHostNode(t *testing.T) {
renderableNodes := render.HostRenderer.Render(fixture.Report).Nodes
renderableNodes := render.HostRenderer.Render(context.Background(), fixture.Report).Nodes
renderableNode := renderableNodes[fixture.ClientHostNodeID]
have := detailed.MakeNode("hosts", detailed.RenderContext{Report: fixture.Report}, renderableNodes, renderableNode)
@@ -177,7 +178,7 @@ func TestMakeDetailedHostNode(t *testing.T) {
func TestMakeDetailedContainerNode(t *testing.T) {
id := fixture.ServerContainerNodeID
renderableNodes := render.ContainerWithImageNameRenderer.Render(fixture.Report).Nodes
renderableNodes := render.ContainerWithImageNameRenderer.Render(context.Background(), fixture.Report).Nodes
renderableNode, ok := renderableNodes[id]
if !ok {
t.Fatalf("Node not found: %s", id)
@@ -306,7 +307,7 @@ func TestMakeDetailedContainerNode(t *testing.T) {
func TestMakeDetailedPodNode(t *testing.T) {
id := fixture.ServerPodNodeID
renderableNodes := render.PodRenderer.Render(fixture.Report).Nodes
renderableNodes := render.PodRenderer.Render(context.Background(), fixture.Report).Nodes
renderableNode, ok := renderableNodes[id]
if !ok {
t.Fatalf("Node not found: %s", id)

View File

@@ -1,6 +1,7 @@
package detailed_test
import (
"context"
"fmt"
"testing"
@@ -14,6 +15,7 @@ import (
)
func TestParents(t *testing.T) {
ctx := context.Background()
for _, c := range []struct {
name string
node report.Node
@@ -21,25 +23,25 @@ func TestParents(t *testing.T) {
}{
{
name: "Node accidentally tagged with itself",
node: render.HostRenderer.Render(fixture.Report).Nodes[fixture.ClientHostNodeID].WithParents(
node: render.HostRenderer.Render(ctx, fixture.Report).Nodes[fixture.ClientHostNodeID].WithParents(
report.MakeSets().Add(report.Host, report.MakeStringSet(fixture.ClientHostNodeID)),
),
want: nil,
},
{
node: render.HostRenderer.Render(fixture.Report).Nodes[fixture.ClientHostNodeID],
node: render.HostRenderer.Render(ctx, fixture.Report).Nodes[fixture.ClientHostNodeID],
want: nil,
},
{
name: "Container image",
node: render.ContainerImageRenderer.Render(fixture.Report).Nodes[expected.ClientContainerImageNodeID],
node: render.ContainerImageRenderer.Render(ctx, fixture.Report).Nodes[expected.ClientContainerImageNodeID],
want: []detailed.Parent{
{ID: fixture.ClientHostNodeID, Label: "client", TopologyID: "hosts"},
},
},
{
name: "Container",
node: render.ContainerWithImageNameRenderer.Render(fixture.Report).Nodes[fixture.ClientContainerNodeID],
node: render.ContainerWithImageNameRenderer.Render(ctx, fixture.Report).Nodes[fixture.ClientContainerNodeID],
want: []detailed.Parent{
{ID: expected.ClientContainerImageNodeID, Label: fixture.ClientContainerImageName, TopologyID: "containers-by-image"},
{ID: fixture.ClientPodNodeID, Label: "pong-a", TopologyID: "pods"},
@@ -47,7 +49,7 @@ func TestParents(t *testing.T) {
},
},
{
node: render.ProcessRenderer.Render(fixture.Report).Nodes[fixture.ClientProcess1NodeID],
node: render.ProcessRenderer.Render(ctx, fixture.Report).Nodes[fixture.ClientProcess1NodeID],
want: []detailed.Parent{
{ID: fixture.ClientContainerNodeID, Label: fixture.ClientContainerName, TopologyID: "containers"},
{ID: fixture.ClientHostNodeID, Label: "client", TopologyID: "hosts"},

View File

@@ -1,9 +1,12 @@
package detailed
import (
"context"
"fmt"
"strings"
opentracing "github.com/opentracing/opentracing-go"
"github.com/weaveworks/scope/probe/awsecs"
"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/probe/kubernetes"
@@ -446,7 +449,9 @@ func (s nodeSummariesByID) Less(i, j int) bool { return s[i].ID < s[j].ID }
type NodeSummaries map[string]NodeSummary
// Summaries converts RenderableNodes into a set of NodeSummaries
func Summaries(rc RenderContext, rns report.Nodes) NodeSummaries {
func Summaries(ctx context.Context, rc RenderContext, rns report.Nodes) NodeSummaries {
span, ctx := opentracing.StartSpanFromContext(ctx, "detailed.Summaries")
defer span.Finish()
result := NodeSummaries{}
for id, node := range rns {

View File

@@ -1,6 +1,7 @@
package detailed_test
import (
"context"
"sort"
"testing"
"time"
@@ -21,7 +22,7 @@ import (
func TestSummaries(t *testing.T) {
{
// Just a convenient source of some rendered nodes
have := detailed.Summaries(detailed.RenderContext{Report: fixture.Report}, render.ProcessRenderer.Render(fixture.Report).Nodes)
have := detailed.Summaries(context.Background(), detailed.RenderContext{Report: fixture.Report}, render.ProcessRenderer.Render(context.Background(), fixture.Report).Nodes)
// The ids of the processes rendered above
expectedIDs := []string{
fixture.ClientProcess1NodeID,
@@ -53,7 +54,7 @@ func TestSummaries(t *testing.T) {
processNode.Metrics = processNode.Metrics.Copy()
processNode.Metrics[process.CPUUsage] = metric
input.Process.Nodes[fixture.ClientProcess1NodeID] = processNode
have := detailed.Summaries(detailed.RenderContext{Report: input}, render.ProcessRenderer.Render(input).Nodes)
have := detailed.Summaries(context.Background(), detailed.RenderContext{Report: input}, render.ProcessRenderer.Render(context.Background(), input).Nodes)
node, ok := have[fixture.ClientProcess1NodeID]
if !ok {

View File

@@ -1,6 +1,8 @@
package render
import (
"context"
"github.com/weaveworks/scope/report"
)
@@ -27,10 +29,10 @@ func MapEndpoints(f endpointMapFunc, topology string) Renderer {
return mapEndpoints{f: f, topology: topology}
}
func (e mapEndpoints) Render(rpt report.Report) Nodes {
func (e mapEndpoints) Render(ctx context.Context, rpt report.Report) Nodes {
local := LocalNetworks(rpt)
endpoints := SelectEndpoint.Render(rpt)
ret := newJoinResults(TopologySelector(e.topology).Render(rpt).Nodes)
endpoints := SelectEndpoint.Render(ctx, rpt)
ret := newJoinResults(TopologySelector(e.topology).Render(ctx, rpt).Nodes)
for _, n := range endpoints.Nodes {
// Nodes without a hostid are mapped to pseudo nodes, if

View File

@@ -1,6 +1,7 @@
package render
import (
"context"
"strings"
"github.com/weaveworks/common/mtime"
@@ -23,8 +24,8 @@ type CustomRenderer struct {
}
// Render implements Renderer
func (c CustomRenderer) Render(rpt report.Report) Nodes {
return c.RenderFunc(c.Renderer.Render(rpt))
func (c CustomRenderer) Render(ctx context.Context, rpt report.Report) Nodes {
return c.RenderFunc(c.Renderer.Render(ctx, rpt))
}
// FilterFunc is the function type used by Filters
@@ -112,8 +113,8 @@ func MakeFilterPseudo(f FilterFunc, r Renderer) Renderer {
}
// Render implements Renderer
func (f Filter) Render(rpt report.Report) Nodes {
return f.FilterFunc.Transform(f.Renderer.Render(rpt))
func (f Filter) Render(ctx context.Context, rpt report.Report) Nodes {
return f.FilterFunc.Transform(f.Renderer.Render(ctx, rpt))
}
// IsConnectedMark is the key added to Node.Metadata by

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"testing"
"github.com/weaveworks/common/test"
@@ -23,7 +24,7 @@ func TestFilterRender(t *testing.T) {
"baz": report.MakeNode("baz"),
}}
have := report.MakeIDList()
for id := range render.Render(report.MakeReport(), render.ColorConnected(renderer), render.FilterFunc(render.IsConnected)).Nodes {
for id := range render.Render(context.Background(), report.MakeReport(), render.ColorConnected(renderer), render.FilterFunc(render.IsConnected)).Nodes {
have = have.Add(id)
}
want := report.MakeIDList("foo", "bar")
@@ -39,13 +40,14 @@ func TestFilterRender2(t *testing.T) {
"bar": report.MakeNode("bar").WithAdjacent("foo"),
"baz": report.MakeNode("baz"),
}}
have := render.Render(report.MakeReport(), renderer, filterBar).Nodes
have := render.Render(context.Background(), report.MakeReport(), renderer, filterBar).Nodes
if have["foo"].Adjacency.Contains("bar") {
t.Error("adjacencies for removed nodes should have been removed")
}
}
func TestFilterUnconnectedPseudoNodes(t *testing.T) {
ctx := context.Background()
// Test pseudo nodes that are made unconnected by filtering
// are also removed.
{
@@ -56,7 +58,7 @@ func TestFilterUnconnectedPseudoNodes(t *testing.T) {
}
renderer := mockRenderer{Nodes: nodes}
want := nodes
have := render.Render(report.MakeReport(), renderer, render.Transformers(nil)).Nodes
have := render.Render(ctx, report.MakeReport(), renderer, render.Transformers(nil)).Nodes
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -67,7 +69,7 @@ func TestFilterUnconnectedPseudoNodes(t *testing.T) {
"bar": report.MakeNode("bar").WithAdjacent("baz"),
"baz": report.MakeNode("baz").WithTopology(render.Pseudo),
}}
have := render.Render(report.MakeReport(), renderer, filterBar).Nodes
have := render.Render(ctx, report.MakeReport(), renderer, filterBar).Nodes
if _, ok := have["baz"]; ok {
t.Error("expected the unconnected pseudonode baz to have been removed")
}
@@ -78,7 +80,7 @@ func TestFilterUnconnectedPseudoNodes(t *testing.T) {
"bar": report.MakeNode("bar").WithAdjacent("foo"),
"baz": report.MakeNode("baz").WithTopology(render.Pseudo).WithAdjacent("bar"),
}}
have := render.Render(report.MakeReport(), renderer, filterBar).Nodes
have := render.Render(ctx, report.MakeReport(), renderer, filterBar).Nodes
if _, ok := have["baz"]; ok {
t.Error("expected the unconnected pseudonode baz to have been removed")
}
@@ -92,7 +94,7 @@ func TestFilterUnconnectedSelf(t *testing.T) {
"foo": report.MakeNode("foo").WithAdjacent("foo"),
}
renderer := mockRenderer{Nodes: nodes}
have := render.Render(report.MakeReport(), render.ColorConnected(renderer), render.FilterFunc(render.IsConnected)).Nodes
have := render.Render(context.Background(), report.MakeReport(), render.ColorConnected(renderer), render.FilterFunc(render.IsConnected)).Nodes
if len(have) > 0 {
t.Error("expected node only connected to self to be removed")
}

17
render/func_name.go Normal file
View File

@@ -0,0 +1,17 @@
package render
import (
"reflect"
"runtime"
)
func functionName(i interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}
func typeName(i interface{}) string {
if m, ok := i.(*memoise); ok {
return "memoise:" + typeName(m.Renderer)
}
return reflect.TypeOf(i).String()
}

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"testing"
"github.com/weaveworks/common/test"
@@ -12,7 +13,7 @@ import (
)
func TestHostRenderer(t *testing.T) {
have := utils.Prune(render.HostRenderer.Render(fixture.Report).Nodes)
have := utils.Prune(render.HostRenderer.Render(context.Background(), fixture.Report).Nodes)
want := utils.Prune(expected.RenderedHosts)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))

View File

@@ -1,6 +1,7 @@
package render
import (
"context"
"fmt"
"math/rand"
"sync"
@@ -40,7 +41,7 @@ func Memoise(r Renderer) Renderer {
// retrieves a promise from the cache and returns its value, otherwise
// it stores a new promise and fulfils it by calling through to
// m.Renderer.
func (m *memoise) Render(rpt report.Report) Nodes {
func (m *memoise) Render(ctx context.Context, rpt report.Report) Nodes {
key := fmt.Sprintf("%s-%s", rpt.ID, m.id)
m.Lock()
@@ -53,7 +54,7 @@ func (m *memoise) Render(rpt report.Report) Nodes {
renderCache.Set(key, promise)
m.Unlock()
output := m.Renderer.Render(rpt)
output := m.Renderer.Render(ctx, rpt)
promise.Set(output)

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"testing"
"github.com/weaveworks/common/test"
@@ -9,13 +10,13 @@ import (
"github.com/weaveworks/scope/test/reflect"
)
type renderFunc func(r report.Report) render.Nodes
type renderFunc func(ctx context.Context, r report.Report) render.Nodes
func (f renderFunc) Render(r report.Report) render.Nodes { return f(r) }
func (f renderFunc) Render(ctx context.Context, r report.Report) render.Nodes { return f(ctx, r) }
func TestMemoise(t *testing.T) {
calls := 0
r := renderFunc(func(rpt report.Report) render.Nodes {
r := renderFunc(func(ctx context.Context, rpt report.Report) render.Nodes {
calls++
return render.Nodes{Nodes: report.Nodes{rpt.ID: report.MakeNode(rpt.ID)}}
})
@@ -27,7 +28,8 @@ func TestMemoise(t *testing.T) {
rpt1 := report.MakeReport()
result1 := m.Render(rpt1)
ctx := context.Background()
result1 := m.Render(ctx, rpt1)
// it should have rendered it.
if _, ok := result1.Nodes[rpt1.ID]; !ok {
t.Errorf("Expected rendered report to contain a node, but got: %v", result1)
@@ -36,7 +38,7 @@ func TestMemoise(t *testing.T) {
t.Errorf("Expected renderer to have been called the first time")
}
result2 := m.Render(rpt1)
result2 := m.Render(ctx, rpt1)
if !reflect.DeepEqual(result1, result2) {
t.Errorf("Expected memoised result to be returned: %s", test.Diff(result1, result2))
}
@@ -45,7 +47,7 @@ func TestMemoise(t *testing.T) {
}
rpt2 := report.MakeReport()
result3 := m.Render(rpt2)
result3 := m.Render(ctx, rpt2)
if reflect.DeepEqual(result1, result3) {
t.Errorf("Expected different result for different report, but were the same")
}
@@ -54,7 +56,7 @@ func TestMemoise(t *testing.T) {
}
render.ResetCache()
result4 := m.Render(rpt1)
result4 := m.Render(ctx, rpt1)
if !reflect.DeepEqual(result1, result4) {
t.Errorf("Expected original result to be returned: %s", test.Diff(result1, result4))
}

View File

@@ -1,6 +1,8 @@
package render
import (
"context"
"github.com/weaveworks/scope/report"
)
@@ -17,8 +19,8 @@ type propagateSingleMetrics struct {
r Renderer
}
func (p propagateSingleMetrics) Render(rpt report.Report) Nodes {
nodes := p.r.Render(rpt)
func (p propagateSingleMetrics) Render(ctx context.Context, rpt report.Report) Nodes {
nodes := p.r.Render(ctx, rpt)
outputs := make(report.Nodes, len(nodes.Nodes))
for id, n := range nodes.Nodes {
var first report.Node

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"testing"
"time"
@@ -159,7 +160,7 @@ func TestPropagateSingleMetrics(t *testing.T) {
},
},
} {
got := render.PropagateSingleMetrics(c.topology, mockRenderer{report.Nodes{c.input.ID: c.input}}).Render(report.Report{}).Nodes
got := render.PropagateSingleMetrics(c.topology, mockRenderer{report.Nodes{c.input.ID: c.input}}).Render(context.Background(), report.Report{}).Nodes
if !reflect.DeepEqual(got, c.output) {
t.Errorf("[%s] Diff: %s", c.name, test.Diff(c.output, got))
}

View File

@@ -1,6 +1,8 @@
package render
import (
"context"
"github.com/weaveworks/scope/probe/kubernetes"
"github.com/weaveworks/scope/report"
)
@@ -23,7 +25,7 @@ var VolumesRenderer = volumesRenderer{}
type volumesRenderer struct{}
// Render renders PV & PVC nodes along with adjacency
func (v volumesRenderer) Render(rpt report.Report) Nodes {
func (v volumesRenderer) Render(ctx context.Context, rpt report.Report) Nodes {
nodes := make(report.Nodes)
for id, n := range rpt.PersistentVolumeClaim.Nodes {
volume, _ := n.Latest.Lookup(kubernetes.VolumeName)
@@ -48,7 +50,7 @@ var PodToVolumeRenderer = podToVolumesRenderer{}
type podToVolumesRenderer struct{}
// Render renders the Pod nodes having volumes adjacency.
func (v podToVolumesRenderer) Render(rpt report.Report) Nodes {
func (v podToVolumesRenderer) Render(ctx context.Context, rpt report.Report) Nodes {
nodes := make(report.Nodes)
for podID, podNode := range rpt.Pod.Nodes {
ClaimName, _ := podNode.Latest.Lookup(kubernetes.VolumeClaim)
@@ -72,7 +74,7 @@ var PVCToStorageClassRenderer = pvcToStorageClassRenderer{}
type pvcToStorageClassRenderer struct{}
// Render renders the PVC & Storage Class nodes with adjacency.
func (v pvcToStorageClassRenderer) Render(rpt report.Report) Nodes {
func (v pvcToStorageClassRenderer) Render(ctx context.Context, rpt report.Report) Nodes {
nodes := make(report.Nodes)
for scID, scNode := range rpt.StorageClass.Nodes {
storageClass, _ := scNode.Latest.Lookup(kubernetes.Name)
@@ -95,7 +97,7 @@ var PVToSnapshotRenderer = pvToSnapshotRenderer{}
type pvToSnapshotRenderer struct{}
//Render renders the PV & Snapshot nodes with adjacency.
func (v pvToSnapshotRenderer) Render(rpt report.Report) Nodes {
func (v pvToSnapshotRenderer) Render(ctx context.Context, rpt report.Report) Nodes {
nodes := make(report.Nodes)
for pvNodeID, p := range rpt.PersistentVolume.Nodes {
volumeName, _ := p.Latest.Lookup(kubernetes.Name)
@@ -119,7 +121,7 @@ type volumeSnapshotRenderer struct{}
// Render renders the volumeSnapshots & volumeSnapshotData with adjacency
// It checks for the volumeSnapshotData name in volumeSnapshot, adjacency is created by matching the volumeSnapshotData name.
func (v volumeSnapshotRenderer) Render(rpt report.Report) Nodes {
func (v volumeSnapshotRenderer) Render(ctx context.Context, rpt report.Report) Nodes {
nodes := make(report.Nodes)
for volumeSnapshotID, volumeSnapshotNode := range rpt.VolumeSnapshot.Nodes {
snapshotData, _ := volumeSnapshotNode.Latest.Lookup(kubernetes.SnapshotData)

View File

@@ -1,6 +1,7 @@
package render
import (
"context"
"strings"
"github.com/weaveworks/scope/probe/docker"
@@ -162,8 +163,8 @@ type Map2Parent struct {
}
// Render implements Renderer
func (m Map2Parent) Render(rpt report.Report) Nodes {
input := m.chainRenderer.Render(rpt)
func (m Map2Parent) Render(ctx context.Context, rpt report.Report) Nodes {
input := m.chainRenderer.Render(ctx, rpt)
ret := newJoinResults(nil)
for _, n := range input.Nodes {

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"testing"
"github.com/weaveworks/common/test"
@@ -13,7 +14,7 @@ import (
)
func TestPodRenderer(t *testing.T) {
have := utils.Prune(render.PodRenderer.Render(fixture.Report).Nodes)
have := utils.Prune(render.PodRenderer.Render(context.Background(), fixture.Report).Nodes)
want := utils.Prune(expected.RenderedPods)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -33,7 +34,7 @@ func TestPodFilterRenderer(t *testing.T) {
kubernetes.Namespace: "kube-system",
})
have := utils.Prune(render.Render(input, render.PodRenderer, filterNonKubeSystem).Nodes)
have := utils.Prune(render.Render(context.Background(), input, render.PodRenderer, filterNonKubeSystem).Nodes)
want := utils.Prune(expected.RenderedPods.Copy())
delete(want, fixture.ClientPodNodeID)
if !reflect.DeepEqual(want, have) {
@@ -42,7 +43,7 @@ func TestPodFilterRenderer(t *testing.T) {
}
func TestPodServiceRenderer(t *testing.T) {
have := utils.Prune(render.PodServiceRenderer.Render(fixture.Report).Nodes)
have := utils.Prune(render.PodServiceRenderer.Render(context.Background(), fixture.Report).Nodes)
want := utils.Prune(expected.RenderedPodServices)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -57,7 +58,7 @@ func TestPodServiceFilterRenderer(t *testing.T) {
kubernetes.Namespace: "kube-system",
})
have := utils.Prune(render.Render(input, render.PodServiceRenderer, filterNonKubeSystem).Nodes)
have := utils.Prune(render.Render(context.Background(), input, render.PodServiceRenderer, filterNonKubeSystem).Nodes)
want := utils.Prune(expected.RenderedPodServices.Copy())
delete(want, fixture.ServiceNodeID)
delete(want, render.IncomingInternetID)

View File

@@ -1,6 +1,8 @@
package render
import (
"context"
"github.com/weaveworks/scope/probe/endpoint"
"github.com/weaveworks/scope/probe/process"
"github.com/weaveworks/scope/report"
@@ -43,11 +45,11 @@ var ProcessNameRenderer = ColorConnected(CustomRenderer{RenderFunc: processes2Na
type endpoints2Processes struct {
}
func (e endpoints2Processes) Render(rpt report.Report) Nodes {
func (e endpoints2Processes) Render(ctx context.Context, rpt report.Report) Nodes {
if len(rpt.Process.Nodes) == 0 {
return Nodes{}
}
endpoints := SelectEndpoint.Render(rpt).Nodes
endpoints := SelectEndpoint.Render(ctx, rpt).Nodes
return MapEndpoints(
func(n report.Node) string {
pid, ok := n.Latest.Lookup(process.PID)
@@ -62,7 +64,7 @@ func (e endpoints2Processes) Render(rpt report.Report) Nodes {
return ""
}
return report.MakeProcessNodeID(hostID, pid)
}, report.Process).Render(rpt)
}, report.Process).Render(ctx, rpt)
}
// When there is more than one connection originating from a source

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"testing"
"github.com/weaveworks/common/test"
@@ -12,7 +13,7 @@ import (
)
func TestEndpointRenderer(t *testing.T) {
have := utils.Prune(render.EndpointRenderer.Render(fixture.Report).Nodes)
have := utils.Prune(render.EndpointRenderer.Render(context.Background(), fixture.Report).Nodes)
want := utils.Prune(expected.RenderedEndpoints)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -20,7 +21,7 @@ func TestEndpointRenderer(t *testing.T) {
}
func TestProcessRenderer(t *testing.T) {
have := utils.Prune(render.ProcessRenderer.Render(fixture.Report).Nodes)
have := utils.Prune(render.ProcessRenderer.Render(context.Background(), fixture.Report).Nodes)
want := utils.Prune(expected.RenderedProcesses)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -28,7 +29,7 @@ func TestProcessRenderer(t *testing.T) {
}
func TestProcessNameRenderer(t *testing.T) {
have := utils.Prune(render.ProcessNameRenderer.Render(fixture.Report).Nodes)
have := utils.Prune(render.ProcessNameRenderer.Render(context.Background(), fixture.Report).Nodes)
want := utils.Prune(expected.RenderedProcessNames)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))

View File

@@ -1,6 +1,11 @@
package render
import (
"context"
opentracing "github.com/opentracing/opentracing-go"
otlog "github.com/opentracing/opentracing-go/log"
"github.com/weaveworks/scope/report"
)
@@ -13,7 +18,7 @@ type MapFunc func(report.Node) report.Node
// Renderer is something that can render a report to a set of Nodes.
type Renderer interface {
Render(report.Report) Nodes
Render(context.Context, report.Report) Nodes
}
// Nodes is the result of Rendering
@@ -48,8 +53,10 @@ func (ts Transformers) Transform(nodes Nodes) Nodes {
}
// Render renders the report and then transforms it
func Render(rpt report.Report, renderer Renderer, transformer Transformer) Nodes {
return transformer.Transform(renderer.Render(rpt))
func Render(ctx context.Context, rpt report.Report, renderer Renderer, transformer Transformer) Nodes {
span, ctx := opentracing.StartSpanFromContext(ctx, "Render:"+typeName(renderer))
defer span.Finish()
return transformer.Transform(renderer.Render(ctx, rpt))
}
// Reduce renderer is a Renderer which merges together the output of several
@@ -62,7 +69,9 @@ func MakeReduce(renderers ...Renderer) Renderer {
}
// Render produces a set of Nodes given a Report.
func (r Reduce) Render(rpt report.Report) Nodes {
func (r Reduce) Render(ctx context.Context, rpt report.Report) Nodes {
span, ctx := opentracing.StartSpanFromContext(ctx, "Reduce.Render")
defer span.Finish()
l := len(r)
switch l {
case 0:
@@ -72,7 +81,9 @@ func (r Reduce) Render(rpt report.Report) Nodes {
for _, renderer := range r {
renderer := renderer // Pike!!
go func() {
c <- renderer.Render(rpt)
span, ctx := opentracing.StartSpanFromContext(ctx, typeName(renderer))
c <- renderer.Render(ctx, rpt)
span.Finish()
}()
}
for ; l > 1; l-- {
@@ -98,9 +109,11 @@ func MakeMap(f MapFunc, r Renderer) Renderer {
// Render transforms a set of Nodes produces by another Renderer.
// using a map function
func (m Map) Render(rpt report.Report) Nodes {
func (m Map) Render(ctx context.Context, rpt report.Report) Nodes {
span, ctx := opentracing.StartSpanFromContext(ctx, "Map.Render:"+functionName(m.MapFunc))
defer span.Finish()
var (
input = m.Renderer.Render(rpt)
input = m.Renderer.Render(ctx, rpt)
output = newJoinResults(nil)
)
@@ -111,6 +124,8 @@ func (m Map) Render(rpt report.Report) Nodes {
output.add(inRenderable.ID, outRenderable)
}
}
span.LogFields(otlog.Int("input.nodes", len(input.Nodes)),
otlog.Int("ouput.nodes", len(output.nodes)))
return output.result(input)
}
@@ -129,9 +144,9 @@ func ConditionalRenderer(c Condition, r Renderer) Renderer {
return conditionalRenderer{c, r}
}
func (cr conditionalRenderer) Render(rpt report.Report) Nodes {
func (cr conditionalRenderer) Render(ctx context.Context, rpt report.Report) Nodes {
if cr.Condition(rpt) {
return cr.Renderer.Render(rpt)
return cr.Renderer.Render(ctx, rpt)
}
return Nodes{}
}

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"reflect"
"testing"
@@ -13,7 +14,7 @@ type mockRenderer struct {
report.Nodes
}
func (m mockRenderer) Render(rpt report.Report) render.Nodes {
func (m mockRenderer) Render(_ context.Context, rpt report.Report) render.Nodes {
return render.Nodes{Nodes: m.Nodes}
}
@@ -27,7 +28,7 @@ func TestReduceRender(t *testing.T) {
"foo": report.MakeNode("foo"),
"bar": report.MakeNode("bar"),
}
have := renderer.Render(report.MakeReport()).Nodes
have := renderer.Render(context.Background(), report.MakeReport()).Nodes
if !reflect.DeepEqual(want, have) {
t.Errorf("want %+v, have %+v", want, have)
}
@@ -44,7 +45,7 @@ func TestMapRender1(t *testing.T) {
}},
}
want := report.Nodes{}
have := mapper.Render(report.MakeReport()).Nodes
have := mapper.Render(context.Background(), report.MakeReport()).Nodes
if !reflect.DeepEqual(want, have) {
t.Errorf("want %+v, have %+v", want, have)
}
@@ -64,7 +65,7 @@ func TestMapRender2(t *testing.T) {
want := report.Nodes{
"bar": report.MakeNode("bar"),
}
have := mapper.Render(report.MakeReport()).Nodes
have := mapper.Render(context.Background(), report.MakeReport()).Nodes
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -86,7 +87,7 @@ func TestMapRender3(t *testing.T) {
"_foo": report.MakeNode("_foo").WithAdjacent("_baz"),
"_baz": report.MakeNode("_baz").WithAdjacent("_foo"),
}
have := mapper.Render(report.MakeReport()).Nodes
have := mapper.Render(context.Background(), report.MakeReport()).Nodes
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}

View File

@@ -1,6 +1,8 @@
package render
import (
"context"
"github.com/weaveworks/scope/report"
)
@@ -9,7 +11,7 @@ import (
type TopologySelector string
// Render implements Renderer
func (t TopologySelector) Render(r report.Report) Nodes {
func (t TopologySelector) Render(ctx context.Context, r report.Report) Nodes {
topology, _ := r.Topology(string(t))
return Nodes{Nodes: topology.Nodes}
}

View File

@@ -1,6 +1,7 @@
package render_test
import (
"context"
"fmt"
"testing"
@@ -109,7 +110,7 @@ var (
)
func TestShortLivedInternetNodeConnections(t *testing.T) {
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(rpt).Nodes)
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(context.Background(), rpt).Nodes)
// Conntracked-only connections from the internet should be assigned to the internet pseudonode
internet, ok := have[render.IncomingInternetID]
@@ -123,7 +124,7 @@ func TestShortLivedInternetNodeConnections(t *testing.T) {
}
func TestPauseContainerDiscarded(t *testing.T) {
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(rpt).Nodes)
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(context.Background(), rpt).Nodes)
// There should only be a connection from container1 and the destination should be container2
container1, ok := have[container1NodeID]
if !ok {