mirror of
https://github.com/weaveworks/scope.git
synced 2026-02-14 18:09:59 +00:00
Merge pull request #2947 from weaveworks/simplify-decoration
Decorators, begone!
This commit is contained in:
@@ -337,9 +337,8 @@ type APITopologyOptionGroup struct {
|
||||
NoneLabel string `json:"noneLabel,omitempty"`
|
||||
}
|
||||
|
||||
// Get the render filters to use for this option group as a Decorator, if any.
|
||||
// If second arg is false, no decorator was needed.
|
||||
func (g APITopologyOptionGroup) getFilterDecorator(value string) (render.Decorator, bool) {
|
||||
// Get the render filters to use for this option group, if any, or nil otherwise.
|
||||
func (g APITopologyOptionGroup) filter(value string) render.FilterFunc {
|
||||
selectType := g.SelectType
|
||||
if selectType == "" {
|
||||
selectType = "one"
|
||||
@@ -352,7 +351,7 @@ func (g APITopologyOptionGroup) getFilterDecorator(value string) (render.Decorat
|
||||
values = strings.Split(value, ",")
|
||||
default:
|
||||
log.Errorf("Invalid select type %s for option group %s, ignoring option", selectType, g.ID)
|
||||
return nil, false
|
||||
return nil
|
||||
}
|
||||
filters := []render.FilterFunc{}
|
||||
for _, opt := range g.Options {
|
||||
@@ -375,11 +374,9 @@ func (g APITopologyOptionGroup) getFilterDecorator(value string) (render.Decorat
|
||||
}
|
||||
}
|
||||
if len(filters) == 0 {
|
||||
return nil, false
|
||||
return nil
|
||||
}
|
||||
// Since we've encoded whether to ignore pseudo topologies into each subfilter,
|
||||
// we want no special behaviour for pseudo topologies here, which corresponds to MakePseudo
|
||||
return render.MakeFilterPseudoDecorator(render.AnyFilterFunc(filters...)), true
|
||||
return render.AnyFilterFunc(filters...)
|
||||
}
|
||||
|
||||
// APITopologyOption describes a ¶m=value to a given topology.
|
||||
@@ -488,24 +485,24 @@ func (r *Registry) renderTopologies(rpt report.Report, req *http.Request) []APIT
|
||||
topologies := []APITopologyDesc{}
|
||||
req.ParseForm()
|
||||
r.walk(func(desc APITopologyDesc) {
|
||||
renderer, decorator, _ := r.RendererForTopology(desc.id, req.Form, rpt)
|
||||
desc.Stats = decorateWithStats(rpt, renderer, decorator)
|
||||
renderer, filter, _ := r.RendererForTopology(desc.id, req.Form, rpt)
|
||||
desc.Stats = computeStats(rpt, renderer, filter)
|
||||
for i, sub := range desc.SubTopologies {
|
||||
renderer, decorator, _ := r.RendererForTopology(sub.id, req.Form, rpt)
|
||||
desc.SubTopologies[i].Stats = decorateWithStats(rpt, renderer, decorator)
|
||||
renderer, filter, _ := r.RendererForTopology(sub.id, req.Form, rpt)
|
||||
desc.SubTopologies[i].Stats = computeStats(rpt, renderer, filter)
|
||||
}
|
||||
topologies = append(topologies, desc)
|
||||
})
|
||||
return updateFilters(rpt, topologies)
|
||||
}
|
||||
|
||||
func decorateWithStats(rpt report.Report, renderer render.Renderer, decorator render.Decorator) topologyStats {
|
||||
func computeStats(rpt report.Report, renderer render.Renderer, filter render.FilterFunc) topologyStats {
|
||||
var (
|
||||
nodes int
|
||||
realNodes int
|
||||
edges int
|
||||
)
|
||||
r := renderer.Render(rpt, decorator)
|
||||
r := render.Render(rpt, renderer, filter)
|
||||
for _, n := range r.Nodes {
|
||||
nodes++
|
||||
if n.Topology != render.Pseudo {
|
||||
@@ -522,7 +519,7 @@ func decorateWithStats(rpt report.Report, renderer render.Renderer, decorator re
|
||||
}
|
||||
|
||||
// RendererForTopology ..
|
||||
func (r *Registry) RendererForTopology(topologyID string, values url.Values, rpt report.Report) (render.Renderer, render.Decorator, error) {
|
||||
func (r *Registry) RendererForTopology(topologyID string, values url.Values, rpt report.Report) (render.Renderer, render.FilterFunc, error) {
|
||||
topology, ok := r.get(topologyID)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("topology not found: %s", topologyID)
|
||||
@@ -534,18 +531,15 @@ func (r *Registry) RendererForTopology(topologyID string, values url.Values, rpt
|
||||
return topology.renderer, nil, nil
|
||||
}
|
||||
|
||||
var decorators []render.Decorator
|
||||
var filters []render.FilterFunc
|
||||
for _, group := range topology.Options {
|
||||
value := values.Get(group.ID)
|
||||
if decorator, ok := group.getFilterDecorator(value); ok {
|
||||
decorators = append(decorators, decorator)
|
||||
if filter := group.filter(value); filter != nil {
|
||||
filters = append(filters, filter)
|
||||
}
|
||||
}
|
||||
if len(decorators) > 0 {
|
||||
// Here we tell the topology renderer to apply the filtering decorator
|
||||
// that we construct as a composition of all the selected filters.
|
||||
composedFilterDecorator := render.ComposeDecorators(decorators...)
|
||||
return render.ApplyDecorator(topology.renderer), composedFilterDecorator, nil
|
||||
if len(filters) > 0 {
|
||||
return topology.renderer, render.ComposeFilterFuncs(filters...), nil
|
||||
}
|
||||
return topology.renderer, nil, nil
|
||||
}
|
||||
@@ -558,7 +552,7 @@ func captureReporter(rep Reporter, f reporterHandler) CtxHandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
type rendererHandler func(context.Context, render.Renderer, render.Decorator, report.RenderContext, http.ResponseWriter, *http.Request)
|
||||
type rendererHandler func(context.Context, render.Renderer, render.FilterFunc, report.RenderContext, http.ResponseWriter, *http.Request)
|
||||
|
||||
func (r *Registry) captureRenderer(rep Reporter, f rendererHandler) CtxHandlerFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
@@ -576,11 +570,11 @@ func (r *Registry) captureRenderer(rep Reporter, f rendererHandler) CtxHandlerFu
|
||||
return
|
||||
}
|
||||
req.ParseForm()
|
||||
renderer, decorator, err := r.RendererForTopology(topologyID, req.Form, rpt)
|
||||
renderer, filter, err := r.RendererForTopology(topologyID, req.Form, rpt)
|
||||
if err != nil {
|
||||
respondWith(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
f(ctx, renderer, decorator, RenderContextForReporter(rep, rpt), w, req)
|
||||
f(ctx, renderer, filter, RenderContextForReporter(rep, rpt), w, req)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ func TestRendererForTopologyWithFiltering(t *testing.T) {
|
||||
urlvalues.Set(systemGroupID, customAPITopologyOptionFilterID)
|
||||
urlvalues.Set("stopped", "running")
|
||||
urlvalues.Set("pseudo", "hide")
|
||||
renderer, decorator, err := topologyRegistry.RendererForTopology("containers", urlvalues, fixture.Report)
|
||||
renderer, filter, err := topologyRegistry.RendererForTopology("containers", urlvalues, fixture.Report)
|
||||
if err != nil {
|
||||
t.Fatalf("Topology Registry Report error: %s", err)
|
||||
}
|
||||
@@ -118,7 +118,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(renderer.Render(input, decorator).Nodes)
|
||||
have := utils.Prune(render.Render(input, renderer, filter).Nodes)
|
||||
want := utils.Prune(expected.RenderedContainers.Copy())
|
||||
delete(want, fixture.ClientContainerNodeID)
|
||||
delete(want, render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostID))
|
||||
@@ -140,7 +140,7 @@ func TestRendererForTopologyNoFiltering(t *testing.T) {
|
||||
urlvalues.Set(systemGroupID, customAPITopologyOptionFilterID)
|
||||
urlvalues.Set("stopped", "running")
|
||||
urlvalues.Set("pseudo", "hide")
|
||||
renderer, decorator, err := topologyRegistry.RendererForTopology("containers", urlvalues, fixture.Report)
|
||||
renderer, filter, err := topologyRegistry.RendererForTopology("containers", urlvalues, fixture.Report)
|
||||
if err != nil {
|
||||
t.Fatalf("Topology Registry Report error: %s", err)
|
||||
}
|
||||
@@ -149,7 +149,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(renderer.Render(input, decorator).Nodes)
|
||||
have := utils.Prune(render.Render(input, renderer, filter).Nodes)
|
||||
want := utils.Prune(expected.RenderedContainers.Copy())
|
||||
delete(want, render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostID))
|
||||
delete(want, render.OutgoingInternetID)
|
||||
@@ -178,12 +178,12 @@ func getTestContainerLabelFilterTopologySummary(t *testing.T, exclude bool) (det
|
||||
urlvalues.Set(systemGroupID, customAPITopologyOptionFilterID)
|
||||
urlvalues.Set("stopped", "running")
|
||||
urlvalues.Set("pseudo", "hide")
|
||||
renderer, decorator, err := topologyRegistry.RendererForTopology("containers", urlvalues, fixture.Report)
|
||||
renderer, filter, err := topologyRegistry.RendererForTopology("containers", urlvalues, fixture.Report)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return detailed.Summaries(report.RenderContext{Report: fixture.Report}, renderer.Render(fixture.Report, decorator).Nodes), nil
|
||||
return detailed.Summaries(report.RenderContext{Report: fixture.Report}, render.Render(fixture.Report, renderer, filter).Nodes), nil
|
||||
}
|
||||
|
||||
func TestAPITopologyAddsKubernetes(t *testing.T) {
|
||||
|
||||
@@ -29,27 +29,40 @@ type APINode struct {
|
||||
}
|
||||
|
||||
// Full topology.
|
||||
func handleTopology(ctx context.Context, renderer render.Renderer, decorator render.Decorator, rc report.RenderContext, w http.ResponseWriter, r *http.Request) {
|
||||
func handleTopology(ctx context.Context, renderer render.Renderer, filter render.FilterFunc, rc report.RenderContext, w http.ResponseWriter, r *http.Request) {
|
||||
respondWith(w, http.StatusOK, APITopology{
|
||||
Nodes: detailed.Summaries(rc, renderer.Render(rc.Report, decorator).Nodes),
|
||||
Nodes: detailed.Summaries(rc, render.Render(rc.Report, renderer, filter).Nodes),
|
||||
})
|
||||
}
|
||||
|
||||
// Individual nodes.
|
||||
func handleNode(ctx context.Context, renderer render.Renderer, decorator render.Decorator, rc report.RenderContext, w http.ResponseWriter, r *http.Request) {
|
||||
func handleNode(ctx context.Context, renderer render.Renderer, filter render.FilterFunc, rc report.RenderContext, w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
vars = mux.Vars(r)
|
||||
topologyID = vars["topology"]
|
||||
nodeID = vars["id"]
|
||||
preciousRenderer = render.PreciousNodeRenderer{PreciousNodeID: nodeID, Renderer: renderer}
|
||||
rendered = preciousRenderer.Render(rc.Report, decorator).Nodes
|
||||
node, ok = rendered[nodeID]
|
||||
vars = mux.Vars(r)
|
||||
topologyID = vars["topology"]
|
||||
nodeID = vars["id"]
|
||||
)
|
||||
// We must not lose the node during filtering. We achieve that by
|
||||
// (1) rendering the report with the base renderer, without
|
||||
// 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)
|
||||
node, ok := nodes.Nodes[nodeID]
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
respondWith(w, http.StatusOK, APINode{Node: detailed.MakeNode(topologyID, rc, rendered, node)})
|
||||
if filter != nil {
|
||||
nodes = filter.Apply(nodes)
|
||||
if filteredNode, ok := nodes.Nodes[nodeID]; ok {
|
||||
node = filteredNode
|
||||
} else { // we've lost the node during filtering; put it back
|
||||
nodes.Nodes[nodeID] = node
|
||||
nodes.Filtered--
|
||||
}
|
||||
}
|
||||
respondWith(w, http.StatusOK, APINode{Node: detailed.MakeNode(topologyID, rc, nodes.Nodes, node)})
|
||||
}
|
||||
|
||||
// Websocket for the full topology.
|
||||
@@ -118,12 +131,12 @@ func handleWebsocket(
|
||||
log.Errorf("Error generating report: %v", err)
|
||||
return
|
||||
}
|
||||
renderer, decorator, err := topologyRegistry.RendererForTopology(topologyID, r.Form, re)
|
||||
renderer, filter, err := topologyRegistry.RendererForTopology(topologyID, r.Form, re)
|
||||
if err != nil {
|
||||
log.Errorf("Error generating report: %v", err)
|
||||
return
|
||||
}
|
||||
newTopo := detailed.Summaries(RenderContextForReporter(rep, re), renderer.Render(re, decorator).Nodes)
|
||||
newTopo := detailed.Summaries(RenderContextForReporter(rep, re), render.Render(re, renderer, filter).Nodes)
|
||||
diff := detailed.TopoDiff(previousTopo, newTopo)
|
||||
previousTopo = newTopo
|
||||
|
||||
|
||||
@@ -65,10 +65,10 @@ func BenchmarkTopologyContainers(b *testing.B) {
|
||||
|
||||
func benchmarkOneTopology(b *testing.B, topologyID string) {
|
||||
benchmarkRender(b, func(report report.Report) {
|
||||
renderer, decorator, err := topologyRegistry.RendererForTopology(topologyID, url.Values{}, report)
|
||||
renderer, filter, err := topologyRegistry.RendererForTopology(topologyID, url.Values{}, report)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
renderer.Render(report, decorator)
|
||||
render.Render(report, renderer, filter)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func benchmarkRender(b *testing.B, r render.Renderer) {
|
||||
b.StopTimer()
|
||||
render.ResetCache()
|
||||
b.StartTimer()
|
||||
benchmarkRenderResult = r.Render(report, FilterNoop)
|
||||
benchmarkRenderResult = r.Render(report)
|
||||
if len(benchmarkRenderResult.Nodes) == 0 {
|
||||
b.Errorf("Rendered topology contained no nodes")
|
||||
}
|
||||
|
||||
@@ -53,10 +53,10 @@ type connectionJoin struct {
|
||||
r Renderer
|
||||
}
|
||||
|
||||
func (c connectionJoin) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
func (c connectionJoin) Render(rpt report.Report) Nodes {
|
||||
local := LocalNetworks(rpt)
|
||||
inputNodes := c.r.Render(rpt, dct)
|
||||
endpoints := SelectEndpoint.Render(rpt, dct)
|
||||
inputNodes := c.r.Render(rpt)
|
||||
endpoints := SelectEndpoint.Render(rpt)
|
||||
|
||||
// Collect all the IPs we are trying to map to, and which ID they map from
|
||||
var ipNodes = map[string]string{}
|
||||
@@ -131,9 +131,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, dct Decorator) Nodes {
|
||||
containers := r.Renderer.Render(rpt, dct)
|
||||
images := SelectContainerImage.Render(rpt, dct)
|
||||
func (r containerWithImageNameRenderer) Render(rpt report.Report) Nodes {
|
||||
containers := r.Renderer.Render(rpt)
|
||||
images := SelectContainerImage.Render(rpt)
|
||||
|
||||
outputs := report.Nodes{}
|
||||
for id, c := range containers.Nodes {
|
||||
@@ -285,7 +285,7 @@ func MapProcess2Container(n report.Node, _ report.Networks) report.Nodes {
|
||||
id = MakePseudoNodeID(UncontainedID, report.ExtractHostID(n))
|
||||
node = NewDerivedPseudoNode(id, n)
|
||||
node = propagateLatest(report.HostNodeID, n, node)
|
||||
node = propagateLatest(IsConnected, n, node)
|
||||
node = propagateLatest(IsConnectedMark, n, node)
|
||||
}
|
||||
return report.Nodes{id: node}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,10 @@ import (
|
||||
"github.com/weaveworks/scope/test/utils"
|
||||
)
|
||||
|
||||
// FilterApplication is a Renderer which filters out application nodes.
|
||||
func FilterApplication(r render.Renderer) render.Renderer {
|
||||
return render.MakeFilter(render.IsApplication, r)
|
||||
}
|
||||
|
||||
// FilterSystem is a Renderer which filters out system nodes.
|
||||
func FilterSystem(r render.Renderer) render.Renderer {
|
||||
return render.MakeFilter(render.IsSystem, r)
|
||||
}
|
||||
|
||||
// FilterNoop does nothing.
|
||||
func FilterNoop(r render.Renderer) render.Renderer { return r }
|
||||
var (
|
||||
filterApplication = render.AnyFilterFunc(render.IsPseudoTopology, render.IsApplication)
|
||||
filterSystem = render.AnyFilterFunc(render.IsPseudoTopology, render.IsSystem)
|
||||
)
|
||||
|
||||
func TestMapProcess2Container(t *testing.T) {
|
||||
for _, input := range []testcase{
|
||||
@@ -59,7 +51,7 @@ func testMap(t *testing.T, f render.MapFunc, input testcase) {
|
||||
}
|
||||
|
||||
func TestContainerRenderer(t *testing.T) {
|
||||
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(fixture.Report, FilterNoop).Nodes)
|
||||
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(fixture.Report).Nodes)
|
||||
want := utils.Prune(expected.RenderedContainers)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
@@ -70,13 +62,10 @@ func TestContainerFilterRenderer(t *testing.T) {
|
||||
// tag on of the containers in the topology and ensure
|
||||
// it is filtered out correctly.
|
||||
input := fixture.Report.Copy()
|
||||
renderer := render.ApplyDecorator(render.ContainerWithImageNameRenderer)
|
||||
|
||||
input.Container.Nodes[fixture.ClientContainerNodeID] = input.Container.Nodes[fixture.ClientContainerNodeID].WithLatests(map[string]string{
|
||||
docker.LabelPrefix + "works.weave.role": "system",
|
||||
})
|
||||
|
||||
have := utils.Prune(renderer.Render(input, FilterApplication).Nodes)
|
||||
have := utils.Prune(render.Render(input, render.ContainerWithImageNameRenderer, filterApplication).Nodes)
|
||||
want := utils.Prune(expected.RenderedContainers.Copy())
|
||||
delete(want, fixture.ClientContainerNodeID)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
@@ -85,8 +74,7 @@ func TestContainerFilterRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContainerHostnameRenderer(t *testing.T) {
|
||||
renderer := render.ApplyDecorator(render.ContainerHostnameRenderer)
|
||||
have := utils.Prune(renderer.Render(fixture.Report, FilterNoop).Nodes)
|
||||
have := utils.Prune(render.Render(fixture.Report, render.ContainerHostnameRenderer, nil).Nodes)
|
||||
want := utils.Prune(expected.RenderedContainerHostnames)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
@@ -94,8 +82,7 @@ func TestContainerHostnameRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContainerHostnameFilterRenderer(t *testing.T) {
|
||||
renderer := render.ApplyDecorator(render.ContainerHostnameRenderer)
|
||||
have := utils.Prune(renderer.Render(fixture.Report, FilterSystem).Nodes)
|
||||
have := utils.Prune(render.Render(fixture.Report, render.ContainerHostnameRenderer, filterSystem).Nodes)
|
||||
want := utils.Prune(expected.RenderedContainerHostnames.Copy())
|
||||
delete(want, fixture.ClientContainerHostname)
|
||||
delete(want, fixture.ServerContainerHostname)
|
||||
@@ -106,8 +93,7 @@ func TestContainerHostnameFilterRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContainerImageRenderer(t *testing.T) {
|
||||
renderer := render.ApplyDecorator(render.ContainerImageRenderer)
|
||||
have := utils.Prune(renderer.Render(fixture.Report, FilterNoop).Nodes)
|
||||
have := utils.Prune(render.Render(fixture.Report, render.ContainerImageRenderer, nil).Nodes)
|
||||
want := utils.Prune(expected.RenderedContainerImages)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
@@ -115,8 +101,7 @@ func TestContainerImageRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContainerImageFilterRenderer(t *testing.T) {
|
||||
renderer := render.ApplyDecorator(render.ContainerImageRenderer)
|
||||
have := utils.Prune(renderer.Render(fixture.Report, FilterSystem).Nodes)
|
||||
have := utils.Prune(render.Render(fixture.Report, render.ContainerImageRenderer, filterSystem).Nodes)
|
||||
want := utils.Prune(expected.RenderedContainerHostnames.Copy())
|
||||
delete(want, fixture.ClientContainerHostname)
|
||||
delete(want, fixture.ServerContainerHostname)
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
func child(t *testing.T, r render.Renderer, id string) detailed.NodeSummary {
|
||||
s, ok := detailed.MakeNodeSummary(report.RenderContext{Report: fixture.Report}, r.Render(fixture.Report, nil).Nodes[id])
|
||||
s, ok := detailed.MakeNodeSummary(report.RenderContext{Report: fixture.Report}, r.Render(fixture.Report).Nodes[id])
|
||||
if !ok {
|
||||
t.Fatalf("Expected node %s to be summarizable, but wasn't", id)
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func connectionID(nodeID string, addr string) string {
|
||||
}
|
||||
|
||||
func TestMakeDetailedHostNode(t *testing.T) {
|
||||
renderableNodes := render.HostRenderer.Render(fixture.Report, nil).Nodes
|
||||
renderableNodes := render.HostRenderer.Render(fixture.Report).Nodes
|
||||
renderableNode := renderableNodes[fixture.ClientHostNodeID]
|
||||
have := detailed.MakeNode("hosts", report.RenderContext{Report: fixture.Report}, renderableNodes, renderableNode)
|
||||
|
||||
@@ -178,7 +178,7 @@ func TestMakeDetailedHostNode(t *testing.T) {
|
||||
|
||||
func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
id := fixture.ServerContainerNodeID
|
||||
renderableNodes := render.ContainerWithImageNameRenderer.Render(fixture.Report, nil).Nodes
|
||||
renderableNodes := render.ContainerWithImageNameRenderer.Render(fixture.Report).Nodes
|
||||
renderableNode, ok := renderableNodes[id]
|
||||
if !ok {
|
||||
t.Fatalf("Node not found: %s", id)
|
||||
@@ -308,7 +308,7 @@ func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
|
||||
func TestMakeDetailedPodNode(t *testing.T) {
|
||||
id := fixture.ServerPodNodeID
|
||||
renderableNodes := render.PodRenderer.Render(fixture.Report, nil).Nodes
|
||||
renderableNodes := render.PodRenderer.Render(fixture.Report).Nodes
|
||||
renderableNode, ok := renderableNodes[id]
|
||||
if !ok {
|
||||
t.Fatalf("Node not found: %s", id)
|
||||
|
||||
@@ -21,25 +21,25 @@ func TestParents(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "Node accidentally tagged with itself",
|
||||
node: render.HostRenderer.Render(fixture.Report, nil).Nodes[fixture.ClientHostNodeID].WithParents(
|
||||
node: render.HostRenderer.Render(fixture.Report).Nodes[fixture.ClientHostNodeID].WithParents(
|
||||
report.MakeSets().Add(report.Host, report.MakeStringSet(fixture.ClientHostNodeID)),
|
||||
),
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
node: render.HostRenderer.Render(fixture.Report, nil).Nodes[fixture.ClientHostNodeID],
|
||||
node: render.HostRenderer.Render(fixture.Report).Nodes[fixture.ClientHostNodeID],
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "Container image",
|
||||
node: render.ContainerImageRenderer.Render(fixture.Report, nil).Nodes[expected.ClientContainerImageNodeID],
|
||||
node: render.ContainerImageRenderer.Render(fixture.Report).Nodes[expected.ClientContainerImageNodeID],
|
||||
want: []detailed.Parent{
|
||||
{ID: fixture.ClientHostNodeID, Label: fixture.ClientHostName, TopologyID: "hosts"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Container",
|
||||
node: render.ContainerWithImageNameRenderer.Render(fixture.Report, nil).Nodes[fixture.ClientContainerNodeID],
|
||||
node: render.ContainerWithImageNameRenderer.Render(fixture.Report).Nodes[fixture.ClientContainerNodeID],
|
||||
want: []detailed.Parent{
|
||||
{ID: expected.ClientContainerImageNodeID, Label: fixture.ClientContainerImageName, TopologyID: "containers-by-image"},
|
||||
{ID: fixture.ClientHostNodeID, Label: fixture.ClientHostName, TopologyID: "hosts"},
|
||||
@@ -47,7 +47,7 @@ func TestParents(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
node: render.ProcessRenderer.Render(fixture.Report, nil).Nodes[fixture.ClientProcess1NodeID],
|
||||
node: render.ProcessRenderer.Render(fixture.Report).Nodes[fixture.ClientProcess1NodeID],
|
||||
want: []detailed.Parent{
|
||||
{ID: fixture.ClientContainerNodeID, Label: fixture.ClientContainerName, TopologyID: "containers"},
|
||||
{ID: fixture.ClientHostNodeID, Label: fixture.ClientHostName, TopologyID: "hosts"},
|
||||
|
||||
@@ -209,8 +209,7 @@ func processNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
|
||||
base.LabelMinor = fmt.Sprintf("%s (%s)", report.ExtractHostID(n), pid)
|
||||
}
|
||||
|
||||
_, isConnected := n.Latest.Lookup(render.IsConnected)
|
||||
base.Linkable = isConnected
|
||||
base.Linkable = render.IsConnected(n)
|
||||
return base, true
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
func TestSummaries(t *testing.T) {
|
||||
{
|
||||
// Just a convenient source of some rendered nodes
|
||||
have := detailed.Summaries(report.RenderContext{Report: fixture.Report}, render.ProcessRenderer.Render(fixture.Report, nil).Nodes)
|
||||
have := detailed.Summaries(report.RenderContext{Report: fixture.Report}, render.ProcessRenderer.Render(fixture.Report).Nodes)
|
||||
// The ids of the processes rendered above
|
||||
expectedIDs := []string{
|
||||
fixture.ClientProcess1NodeID,
|
||||
@@ -51,7 +51,7 @@ func TestSummaries(t *testing.T) {
|
||||
input := fixture.Report.Copy()
|
||||
|
||||
input.Process.Nodes[fixture.ClientProcess1NodeID].Metrics[process.CPUUsage] = metric
|
||||
have := detailed.Summaries(report.RenderContext{Report: input}, render.ProcessRenderer.Render(input, nil).Nodes)
|
||||
have := detailed.Summaries(report.RenderContext{Report: input}, render.ProcessRenderer.Render(input).Nodes)
|
||||
|
||||
node, ok := have[fixture.ClientProcess1NodeID]
|
||||
if !ok {
|
||||
|
||||
@@ -14,24 +14,6 @@ const (
|
||||
swarmNamespaceLabel = "com.docker.stack.namespace"
|
||||
)
|
||||
|
||||
// PreciousNodeRenderer ensures a node is never filtered out by decorators
|
||||
type PreciousNodeRenderer struct {
|
||||
PreciousNodeID string
|
||||
Renderer
|
||||
}
|
||||
|
||||
// Render implements Renderer
|
||||
func (p PreciousNodeRenderer) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
undecoratedNodes := p.Renderer.Render(rpt, nil)
|
||||
preciousNode, foundBeforeDecoration := undecoratedNodes.Nodes[p.PreciousNodeID]
|
||||
finalNodes := applyDecorator{ConstantRenderer{undecoratedNodes}}.Render(rpt, dct)
|
||||
if _, ok := finalNodes.Nodes[p.PreciousNodeID]; !ok && foundBeforeDecoration {
|
||||
finalNodes.Nodes[p.PreciousNodeID] = preciousNode
|
||||
finalNodes.Filtered--
|
||||
}
|
||||
return finalNodes
|
||||
}
|
||||
|
||||
// CustomRenderer allow for mapping functions that received the entire topology
|
||||
// in one call - useful for functions that need to consider the entire graph.
|
||||
// We should minimise the use of this renderer type, as it is very inflexible.
|
||||
@@ -41,13 +23,13 @@ type CustomRenderer struct {
|
||||
}
|
||||
|
||||
// Render implements Renderer
|
||||
func (c CustomRenderer) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
return c.RenderFunc(c.Renderer.Render(rpt, dct))
|
||||
func (c CustomRenderer) Render(rpt report.Report) Nodes {
|
||||
return c.RenderFunc(c.Renderer.Render(rpt))
|
||||
}
|
||||
|
||||
// ColorConnected colors nodes with the IsConnected key if
|
||||
// they have edges to or from them. Edges to/from yourself
|
||||
// are not counted here (see #656).
|
||||
// ColorConnected colors nodes with the IsConnectedMark key if they
|
||||
// have edges to or from them. Edges to/from yourself are not counted
|
||||
// here (see #656).
|
||||
func ColorConnected(r Renderer) Renderer {
|
||||
return CustomRenderer{
|
||||
Renderer: r,
|
||||
@@ -66,7 +48,7 @@ func ColorConnected(r Renderer) Renderer {
|
||||
|
||||
output := input.Copy()
|
||||
for id := range connected {
|
||||
output[id] = output[id].WithLatest(IsConnected, mtime.Now(), "true")
|
||||
output[id] = output[id].WithLatest(IsConnectedMark, mtime.Now(), "true")
|
||||
}
|
||||
return Nodes{Nodes: output, Filtered: input.Filtered}
|
||||
},
|
||||
@@ -100,57 +82,13 @@ func ComposeFilterFuncs(fs ...FilterFunc) FilterFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// Filter removes nodes from a view based on a predicate.
|
||||
type Filter struct {
|
||||
Renderer
|
||||
FilterFunc FilterFunc
|
||||
}
|
||||
|
||||
// MakeFilter makes a new Filter (that ignores pseudo nodes).
|
||||
func MakeFilter(f FilterFunc, r Renderer) Renderer {
|
||||
return Filter{
|
||||
Renderer: r,
|
||||
FilterFunc: func(n report.Node) bool {
|
||||
return n.Topology == Pseudo || f(n)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MakeFilterPseudo makes a new Filter that will not ignore pseudo nodes.
|
||||
func MakeFilterPseudo(f FilterFunc, r Renderer) Renderer {
|
||||
return Filter{
|
||||
Renderer: r,
|
||||
FilterFunc: f,
|
||||
}
|
||||
}
|
||||
|
||||
// MakeFilterDecorator makes a decorator that filters out non-pseudo nodes
|
||||
// which match the predicate.
|
||||
func MakeFilterDecorator(f FilterFunc) Decorator {
|
||||
return func(renderer Renderer) Renderer {
|
||||
return MakeFilter(f, renderer)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeFilterPseudoDecorator makes a decorator that filters out all nodes
|
||||
// (including pseudo nodes) which match the predicate.
|
||||
func MakeFilterPseudoDecorator(f FilterFunc) Decorator {
|
||||
return func(renderer Renderer) Renderer {
|
||||
return MakeFilterPseudo(f, renderer)
|
||||
}
|
||||
}
|
||||
|
||||
// Render implements Renderer
|
||||
func (f Filter) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
return f.render(rpt, dct)
|
||||
}
|
||||
|
||||
func (f Filter) render(rpt report.Report, dct Decorator) Nodes {
|
||||
// Apply applies the filter to all nodes
|
||||
func (f FilterFunc) Apply(nodes Nodes) Nodes {
|
||||
output := report.Nodes{}
|
||||
inDegrees := map[string]int{}
|
||||
filtered := 0
|
||||
for id, node := range f.Renderer.Render(rpt, dct).Nodes {
|
||||
if f.FilterFunc(node) {
|
||||
for id, node := range nodes.Nodes {
|
||||
if f(node) {
|
||||
output[id] = node
|
||||
inDegrees[id] = 0
|
||||
} else {
|
||||
@@ -186,9 +124,39 @@ func (f Filter) render(rpt report.Report, dct Decorator) Nodes {
|
||||
return Nodes{Nodes: output, Filtered: filtered}
|
||||
}
|
||||
|
||||
// IsConnected is the key added to Node.Metadata by ColorConnected
|
||||
// to indicate a node has an edge pointing to it or from it
|
||||
const IsConnected = "is_connected"
|
||||
// Filter removes nodes from a view based on a predicate.
|
||||
type Filter struct {
|
||||
Renderer
|
||||
FilterFunc FilterFunc
|
||||
}
|
||||
|
||||
// MakeFilter makes a new Filter (that ignores pseudo nodes).
|
||||
func MakeFilter(f FilterFunc, r Renderer) Renderer {
|
||||
return Filter{
|
||||
Renderer: r,
|
||||
FilterFunc: func(n report.Node) bool {
|
||||
return n.Topology == Pseudo || f(n)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MakeFilterPseudo makes a new Filter that will not ignore pseudo nodes.
|
||||
func MakeFilterPseudo(f FilterFunc, r Renderer) Renderer {
|
||||
return Filter{
|
||||
Renderer: r,
|
||||
FilterFunc: f,
|
||||
}
|
||||
}
|
||||
|
||||
// Render implements Renderer
|
||||
func (f Filter) Render(rpt report.Report) Nodes {
|
||||
return f.FilterFunc.Apply(f.Renderer.Render(rpt))
|
||||
}
|
||||
|
||||
// IsConnectedMark is the key added to Node.Metadata by
|
||||
// ColorConnected to indicate a node has an edge pointing to it or
|
||||
// from it
|
||||
const IsConnectedMark = "is_connected"
|
||||
|
||||
// Complement takes a FilterFunc f and returns a FilterFunc that has the same
|
||||
// effects, if any, and returns the opposite truth value.
|
||||
@@ -196,16 +164,17 @@ func Complement(f FilterFunc) FilterFunc {
|
||||
return func(node report.Node) bool { return !f(node) }
|
||||
}
|
||||
|
||||
// IsConnected checks whether the node has been marked with the
|
||||
// IsConnectedMark.
|
||||
func IsConnected(node report.Node) bool {
|
||||
_, ok := node.Latest.Lookup(IsConnectedMark)
|
||||
return ok
|
||||
}
|
||||
|
||||
// FilterUnconnected produces a renderer that filters unconnected nodes
|
||||
// from the given renderer
|
||||
func FilterUnconnected(r Renderer) Renderer {
|
||||
return MakeFilterPseudo(
|
||||
func(node report.Node) bool {
|
||||
_, ok := node.Latest.Lookup(IsConnected)
|
||||
return ok
|
||||
},
|
||||
ColorConnected(r),
|
||||
)
|
||||
return MakeFilterPseudo(IsConnected, ColorConnected(r))
|
||||
}
|
||||
|
||||
// FilterUnconnectedPseudo produces a renderer that filters
|
||||
@@ -216,8 +185,7 @@ func FilterUnconnectedPseudo(r Renderer) Renderer {
|
||||
if !IsPseudoTopology(node) {
|
||||
return true
|
||||
}
|
||||
_, ok := node.Latest.Lookup(IsConnected)
|
||||
return ok
|
||||
return IsConnected(node)
|
||||
},
|
||||
ColorConnected(r),
|
||||
)
|
||||
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
"github.com/weaveworks/scope/test/reflect"
|
||||
)
|
||||
|
||||
func isNotBar(node report.Node) bool {
|
||||
return node.ID != "bar"
|
||||
}
|
||||
|
||||
func TestFilterRender(t *testing.T) {
|
||||
renderer := mockRenderer{Nodes: report.Nodes{
|
||||
"foo": report.MakeNode("foo").WithAdjacent("bar"),
|
||||
@@ -16,7 +20,7 @@ func TestFilterRender(t *testing.T) {
|
||||
"baz": report.MakeNode("baz"),
|
||||
}}
|
||||
have := report.MakeIDList()
|
||||
for id := range renderer.Render(report.MakeReport(), render.FilterUnconnected).Nodes {
|
||||
for id := range render.Render(report.MakeReport(), render.ColorConnected(renderer), render.IsConnected).Nodes {
|
||||
have = have.Add(id)
|
||||
}
|
||||
want := report.MakeIDList("foo", "bar")
|
||||
@@ -27,21 +31,12 @@ func TestFilterRender(t *testing.T) {
|
||||
|
||||
func TestFilterRender2(t *testing.T) {
|
||||
// Test adjacencies are removed for filtered nodes.
|
||||
filter := func(renderer render.Renderer) render.Renderer {
|
||||
return &render.Filter{
|
||||
FilterFunc: func(node report.Node) bool {
|
||||
return node.ID != "bar"
|
||||
},
|
||||
Renderer: renderer,
|
||||
}
|
||||
}
|
||||
renderer := mockRenderer{Nodes: report.Nodes{
|
||||
"foo": report.MakeNode("foo").WithAdjacent("bar"),
|
||||
"bar": report.MakeNode("bar").WithAdjacent("foo"),
|
||||
"baz": report.MakeNode("baz"),
|
||||
}}
|
||||
|
||||
have := renderer.Render(report.MakeReport(), filter).Nodes
|
||||
have := render.Render(report.MakeReport(), renderer, isNotBar).Nodes
|
||||
if have["foo"].Adjacency.Contains("bar") {
|
||||
t.Error("adjacencies for removed nodes should have been removed")
|
||||
}
|
||||
@@ -57,54 +52,30 @@ func TestFilterUnconnectedPseudoNodes(t *testing.T) {
|
||||
"baz": report.MakeNode("baz").WithTopology(render.Pseudo),
|
||||
}
|
||||
renderer := mockRenderer{Nodes: nodes}
|
||||
filter := func(renderer render.Renderer) render.Renderer {
|
||||
return &render.Filter{
|
||||
FilterFunc: func(node report.Node) bool {
|
||||
return true
|
||||
},
|
||||
Renderer: renderer,
|
||||
}
|
||||
}
|
||||
want := nodes
|
||||
have := renderer.Render(report.MakeReport(), filter).Nodes
|
||||
have := render.Render(report.MakeReport(), renderer, nil).Nodes
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
{
|
||||
filter := func(renderer render.Renderer) render.Renderer {
|
||||
return &render.Filter{
|
||||
FilterFunc: func(node report.Node) bool {
|
||||
return node.ID != "bar"
|
||||
},
|
||||
Renderer: renderer,
|
||||
}
|
||||
}
|
||||
renderer := mockRenderer{Nodes: report.Nodes{
|
||||
"foo": report.MakeNode("foo").WithAdjacent("bar"),
|
||||
"bar": report.MakeNode("bar").WithAdjacent("baz"),
|
||||
"baz": report.MakeNode("baz").WithTopology(render.Pseudo),
|
||||
}}
|
||||
have := renderer.Render(report.MakeReport(), filter).Nodes
|
||||
have := render.Render(report.MakeReport(), renderer, isNotBar).Nodes
|
||||
if _, ok := have["baz"]; ok {
|
||||
t.Error("expected the unconnected pseudonode baz to have been removed")
|
||||
}
|
||||
}
|
||||
{
|
||||
filter := func(renderer render.Renderer) render.Renderer {
|
||||
return &render.Filter{
|
||||
FilterFunc: func(node report.Node) bool {
|
||||
return node.ID != "bar"
|
||||
},
|
||||
Renderer: renderer,
|
||||
}
|
||||
}
|
||||
renderer := mockRenderer{Nodes: report.Nodes{
|
||||
"foo": report.MakeNode("foo"),
|
||||
"bar": report.MakeNode("bar").WithAdjacent("foo"),
|
||||
"baz": report.MakeNode("baz").WithTopology(render.Pseudo).WithAdjacent("bar"),
|
||||
}}
|
||||
have := renderer.Render(report.MakeReport(), filter).Nodes
|
||||
have := render.Render(report.MakeReport(), renderer, isNotBar).Nodes
|
||||
if _, ok := have["baz"]; ok {
|
||||
t.Error("expected the unconnected pseudonode baz to have been removed")
|
||||
}
|
||||
@@ -118,7 +89,7 @@ func TestFilterUnconnectedSelf(t *testing.T) {
|
||||
"foo": report.MakeNode("foo").WithAdjacent("foo"),
|
||||
}
|
||||
renderer := mockRenderer{Nodes: nodes}
|
||||
have := renderer.Render(report.MakeReport(), render.FilterUnconnected).Nodes
|
||||
have := render.Render(report.MakeReport(), render.ColorConnected(renderer), render.IsConnected).Nodes
|
||||
if len(have) > 0 {
|
||||
t.Error("expected node only connected to self to be removed")
|
||||
}
|
||||
|
||||
@@ -66,9 +66,9 @@ func MapX2Host(n report.Node, _ report.Networks) report.Nodes {
|
||||
type endpoints2Hosts struct {
|
||||
}
|
||||
|
||||
func (e endpoints2Hosts) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
func (e endpoints2Hosts) Render(rpt report.Report) Nodes {
|
||||
local := LocalNetworks(rpt)
|
||||
endpoints := SelectEndpoint.Render(rpt, dct)
|
||||
endpoints := SelectEndpoint.Render(rpt)
|
||||
ret := newJoinResults()
|
||||
|
||||
for _, n := range endpoints.Nodes {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func TestHostRenderer(t *testing.T) {
|
||||
have := utils.Prune(render.HostRenderer.Render(fixture.Report, FilterNoop).Nodes)
|
||||
have := utils.Prune(render.HostRenderer.Render(fixture.Report).Nodes)
|
||||
want := utils.Prune(expected.RenderedHosts)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
|
||||
@@ -37,13 +37,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.
|
||||
//
|
||||
// The cache is bypassed when rendering a report with a decorator.
|
||||
func (m *memoise) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
if dct != nil {
|
||||
return m.Renderer.Render(rpt, dct)
|
||||
}
|
||||
|
||||
func (m *memoise) Render(rpt report.Report) Nodes {
|
||||
key := fmt.Sprintf("%s-%s", rpt.ID, m.id)
|
||||
|
||||
m.Lock()
|
||||
@@ -56,7 +50,7 @@ func (m *memoise) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
renderCache.Set(key, promise)
|
||||
m.Unlock()
|
||||
|
||||
output := m.Renderer.Render(rpt, dct)
|
||||
output := m.Renderer.Render(rpt)
|
||||
|
||||
promise.Set(output)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
type renderFunc func(r report.Report) render.Nodes
|
||||
|
||||
func (f renderFunc) Render(r report.Report, _ render.Decorator) render.Nodes { return f(r) }
|
||||
func (f renderFunc) Render(r report.Report) render.Nodes { return f(r) }
|
||||
|
||||
func TestMemoise(t *testing.T) {
|
||||
calls := 0
|
||||
@@ -22,7 +22,7 @@ func TestMemoise(t *testing.T) {
|
||||
m := render.Memoise(r)
|
||||
rpt1 := report.MakeReport()
|
||||
|
||||
result1 := m.Render(rpt1, nil)
|
||||
result1 := m.Render(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)
|
||||
@@ -31,7 +31,7 @@ func TestMemoise(t *testing.T) {
|
||||
t.Errorf("Expected renderer to have been called the first time")
|
||||
}
|
||||
|
||||
result2 := m.Render(rpt1, nil)
|
||||
result2 := m.Render(rpt1)
|
||||
if !reflect.DeepEqual(result1, result2) {
|
||||
t.Errorf("Expected memoised result to be returned: %s", test.Diff(result1, result2))
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func TestMemoise(t *testing.T) {
|
||||
}
|
||||
|
||||
rpt2 := report.MakeReport()
|
||||
result3 := m.Render(rpt2, nil)
|
||||
result3 := m.Render(rpt2)
|
||||
if reflect.DeepEqual(result1, result3) {
|
||||
t.Errorf("Expected different result for different report, but were the same")
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func TestMemoise(t *testing.T) {
|
||||
}
|
||||
|
||||
render.ResetCache()
|
||||
result4 := m.Render(rpt1, nil)
|
||||
result4 := m.Render(rpt1)
|
||||
if !reflect.DeepEqual(result1, result4) {
|
||||
t.Errorf("Expected original result to be returned: %s", test.Diff(result1, result4))
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ func renderParents(childTopology string, parentTopologies []string, noParentsPse
|
||||
// to deployments where applicable.
|
||||
type selectPodsWithDeployments struct{}
|
||||
|
||||
func (s selectPodsWithDeployments) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
func (s selectPodsWithDeployments) Render(rpt report.Report) Nodes {
|
||||
result := report.Nodes{}
|
||||
// For each pod, we check for any replica sets, and merge any deployments they point to
|
||||
// into a replacement Parents value.
|
||||
|
||||
@@ -13,28 +13,24 @@ import (
|
||||
)
|
||||
|
||||
func TestPodRenderer(t *testing.T) {
|
||||
have := utils.Prune(render.PodRenderer.Render(fixture.Report, nil).Nodes)
|
||||
have := utils.Prune(render.PodRenderer.Render(fixture.Report).Nodes)
|
||||
want := utils.Prune(expected.RenderedPods)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
|
||||
func filterNonKubeSystem(renderer render.Renderer) render.Renderer {
|
||||
return render.MakeFilter(render.Complement(render.IsNamespace("kube-system")), renderer)
|
||||
}
|
||||
var filterNonKubeSystem = render.Complement(render.IsNamespace("kube-system"))
|
||||
|
||||
func TestPodFilterRenderer(t *testing.T) {
|
||||
// tag on containers or pod namespace in the topology and ensure
|
||||
// it is filtered out correctly.
|
||||
input := fixture.Report.Copy()
|
||||
renderer := render.ApplyDecorator(render.PodRenderer)
|
||||
|
||||
input.Pod.Nodes[fixture.ClientPodNodeID] = input.Pod.Nodes[fixture.ClientPodNodeID].WithLatests(map[string]string{
|
||||
kubernetes.Namespace: "kube-system",
|
||||
})
|
||||
|
||||
have := utils.Prune(renderer.Render(input, filterNonKubeSystem).Nodes)
|
||||
have := utils.Prune(render.Render(input, render.PodRenderer, filterNonKubeSystem).Nodes)
|
||||
want := utils.Prune(expected.RenderedPods.Copy())
|
||||
delete(want, fixture.ClientPodNodeID)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
@@ -43,7 +39,7 @@ func TestPodFilterRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPodServiceRenderer(t *testing.T) {
|
||||
have := utils.Prune(render.PodServiceRenderer.Render(fixture.Report, nil).Nodes)
|
||||
have := utils.Prune(render.PodServiceRenderer.Render(fixture.Report).Nodes)
|
||||
want := utils.Prune(expected.RenderedPodServices)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
@@ -54,13 +50,11 @@ func TestPodServiceFilterRenderer(t *testing.T) {
|
||||
// tag on containers or pod namespace in the topology and ensure
|
||||
// it is filtered out correctly.
|
||||
input := fixture.Report.Copy()
|
||||
renderer := render.ApplyDecorator(render.PodServiceRenderer)
|
||||
|
||||
input.Service.Nodes[fixture.ServiceNodeID] = input.Service.Nodes[fixture.ServiceNodeID].WithLatests(map[string]string{
|
||||
kubernetes.Namespace: "kube-system",
|
||||
})
|
||||
|
||||
have := utils.Prune(renderer.Render(input, filterNonKubeSystem).Nodes)
|
||||
have := utils.Prune(render.Render(input, render.PodServiceRenderer, filterNonKubeSystem).Nodes)
|
||||
want := utils.Prune(expected.RenderedPodServices.Copy())
|
||||
delete(want, fixture.ServiceNodeID)
|
||||
delete(want, render.IncomingInternetID)
|
||||
|
||||
@@ -40,9 +40,9 @@ type processWithContainerNameRenderer struct {
|
||||
Renderer
|
||||
}
|
||||
|
||||
func (r processWithContainerNameRenderer) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
processes := r.Renderer.Render(rpt, dct)
|
||||
containers := SelectContainer.Render(rpt, dct)
|
||||
func (r processWithContainerNameRenderer) Render(rpt report.Report) Nodes {
|
||||
processes := r.Renderer.Render(rpt)
|
||||
containers := SelectContainer.Render(rpt)
|
||||
|
||||
outputs := report.Nodes{}
|
||||
for id, p := range processes.Nodes {
|
||||
@@ -86,13 +86,13 @@ var ProcessNameRenderer = ConditionalRenderer(renderProcesses,
|
||||
type endpoints2Processes struct {
|
||||
}
|
||||
|
||||
func (e endpoints2Processes) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
func (e endpoints2Processes) Render(rpt report.Report) Nodes {
|
||||
if len(rpt.Process.Nodes) == 0 {
|
||||
return Nodes{}
|
||||
}
|
||||
local := LocalNetworks(rpt)
|
||||
processes := SelectProcess.Render(rpt, dct)
|
||||
endpoints := SelectEndpoint.Render(rpt, dct)
|
||||
processes := SelectProcess.Render(rpt)
|
||||
endpoints := SelectEndpoint.Render(rpt)
|
||||
ret := newJoinResults()
|
||||
|
||||
for _, n := range endpoints.Nodes {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func TestEndpointRenderer(t *testing.T) {
|
||||
have := utils.Prune(render.EndpointRenderer.Render(fixture.Report, FilterNoop).Nodes)
|
||||
have := utils.Prune(render.EndpointRenderer.Render(fixture.Report).Nodes)
|
||||
want := utils.Prune(expected.RenderedEndpoints)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
@@ -20,7 +20,7 @@ func TestEndpointRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcessRenderer(t *testing.T) {
|
||||
have := utils.Prune(render.ProcessRenderer.Render(fixture.Report, FilterNoop).Nodes)
|
||||
have := utils.Prune(render.ProcessRenderer.Render(fixture.Report).Nodes)
|
||||
want := utils.Prune(expected.RenderedProcesses)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
@@ -28,7 +28,7 @@ func TestProcessRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcessNameRenderer(t *testing.T) {
|
||||
have := utils.Prune(render.ProcessNameRenderer.Render(fixture.Report, FilterNoop).Nodes)
|
||||
have := utils.Prune(render.ProcessNameRenderer.Render(fixture.Report).Nodes)
|
||||
want := utils.Prune(expected.RenderedProcessNames)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
|
||||
@@ -12,7 +12,7 @@ type MapFunc func(report.Node, report.Networks) report.Nodes
|
||||
|
||||
// Renderer is something that can render a report to a set of Nodes.
|
||||
type Renderer interface {
|
||||
Render(report.Report, Decorator) Nodes
|
||||
Render(report.Report) Nodes
|
||||
}
|
||||
|
||||
// Nodes is the result of Rendering
|
||||
@@ -29,6 +29,15 @@ func (r Nodes) Merge(o Nodes) Nodes {
|
||||
}
|
||||
}
|
||||
|
||||
// Render renders the report and then applies the filter
|
||||
func Render(rpt report.Report, renderer Renderer, filter FilterFunc) Nodes {
|
||||
nodes := renderer.Render(rpt)
|
||||
if filter != nil {
|
||||
nodes = filter.Apply(nodes)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
// Reduce renderer is a Renderer which merges together the output of several
|
||||
// other renderers.
|
||||
type Reduce []Renderer
|
||||
@@ -39,7 +48,7 @@ func MakeReduce(renderers ...Renderer) Renderer {
|
||||
}
|
||||
|
||||
// Render produces a set of Nodes given a Report.
|
||||
func (r Reduce) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
func (r Reduce) Render(rpt report.Report) Nodes {
|
||||
l := len(r)
|
||||
switch l {
|
||||
case 0:
|
||||
@@ -49,7 +58,7 @@ func (r Reduce) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
for _, renderer := range r {
|
||||
renderer := renderer // Pike!!
|
||||
go func() {
|
||||
c <- renderer.Render(rpt, dct)
|
||||
c <- renderer.Render(rpt)
|
||||
}()
|
||||
}
|
||||
for ; l > 1; l-- {
|
||||
@@ -75,9 +84,9 @@ 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, dct Decorator) Nodes {
|
||||
func (m Map) Render(rpt report.Report) Nodes {
|
||||
var (
|
||||
input = m.Renderer.Render(rpt, dct)
|
||||
input = m.Renderer.Render(rpt)
|
||||
output = report.Nodes{}
|
||||
mapped = map[string]report.IDList{} // input node ID -> output node IDs
|
||||
adjacencies = map[string]report.IDList{} // output node ID -> input node Adjacencies
|
||||
@@ -111,35 +120,6 @@ func (m Map) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
return Nodes{Nodes: output}
|
||||
}
|
||||
|
||||
// Decorator transforms one renderer to another. e.g. Filters.
|
||||
type Decorator func(Renderer) Renderer
|
||||
|
||||
// ComposeDecorators composes decorators into one.
|
||||
func ComposeDecorators(decorators ...Decorator) Decorator {
|
||||
return func(r Renderer) Renderer {
|
||||
for _, decorator := range decorators {
|
||||
r = decorator(r)
|
||||
}
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
type applyDecorator struct {
|
||||
Renderer
|
||||
}
|
||||
|
||||
func (ad applyDecorator) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
if dct != nil {
|
||||
return dct(ad.Renderer).Render(rpt, nil)
|
||||
}
|
||||
return ad.Renderer.Render(rpt, nil)
|
||||
}
|
||||
|
||||
// ApplyDecorator returns a renderer which will apply the given decorator to the child render.
|
||||
func ApplyDecorator(renderer Renderer) Renderer {
|
||||
return applyDecorator{renderer}
|
||||
}
|
||||
|
||||
func propagateLatest(key string, from, to report.Node) report.Node {
|
||||
if value, timestamp, ok := from.Latest.LookupEntry(key); ok {
|
||||
to.Latest = to.Latest.Set(key, timestamp, value)
|
||||
@@ -161,23 +141,13 @@ func ConditionalRenderer(c Condition, r Renderer) Renderer {
|
||||
return conditionalRenderer{c, r}
|
||||
}
|
||||
|
||||
func (cr conditionalRenderer) Render(rpt report.Report, dct Decorator) Nodes {
|
||||
func (cr conditionalRenderer) Render(rpt report.Report) Nodes {
|
||||
if cr.Condition(rpt) {
|
||||
return cr.Renderer.Render(rpt, dct)
|
||||
return cr.Renderer.Render(rpt)
|
||||
}
|
||||
return Nodes{}
|
||||
}
|
||||
|
||||
// ConstantRenderer renders a fixed set of nodes
|
||||
type ConstantRenderer struct {
|
||||
Nodes
|
||||
}
|
||||
|
||||
// Render implements Renderer
|
||||
func (c ConstantRenderer) Render(_ report.Report, _ Decorator) Nodes {
|
||||
return c.Nodes
|
||||
}
|
||||
|
||||
// joinResults is used by Renderers that join sets of nodes
|
||||
type joinResults struct {
|
||||
nodes report.Nodes
|
||||
|
||||
@@ -13,10 +13,7 @@ type mockRenderer struct {
|
||||
report.Nodes
|
||||
}
|
||||
|
||||
func (m mockRenderer) Render(rpt report.Report, d render.Decorator) render.Nodes {
|
||||
if d != nil {
|
||||
return d(mockRenderer{m.Nodes}).Render(rpt, nil)
|
||||
}
|
||||
func (m mockRenderer) Render(rpt report.Report) render.Nodes {
|
||||
return render.Nodes{Nodes: m.Nodes}
|
||||
}
|
||||
|
||||
@@ -30,7 +27,7 @@ func TestReduceRender(t *testing.T) {
|
||||
"foo": report.MakeNode("foo"),
|
||||
"bar": report.MakeNode("bar"),
|
||||
}
|
||||
have := renderer.Render(report.MakeReport(), FilterNoop).Nodes
|
||||
have := renderer.Render(report.MakeReport()).Nodes
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("want %+v, have %+v", want, have)
|
||||
}
|
||||
@@ -47,7 +44,7 @@ func TestMapRender1(t *testing.T) {
|
||||
}},
|
||||
}
|
||||
want := report.Nodes{}
|
||||
have := mapper.Render(report.MakeReport(), FilterNoop).Nodes
|
||||
have := mapper.Render(report.MakeReport()).Nodes
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("want %+v, have %+v", want, have)
|
||||
}
|
||||
@@ -69,7 +66,7 @@ func TestMapRender2(t *testing.T) {
|
||||
want := report.Nodes{
|
||||
"bar": report.MakeNode("bar"),
|
||||
}
|
||||
have := mapper.Render(report.MakeReport(), FilterNoop).Nodes
|
||||
have := mapper.Render(report.MakeReport()).Nodes
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
@@ -91,7 +88,7 @@ func TestMapRender3(t *testing.T) {
|
||||
"_foo": report.MakeNode("_foo").WithAdjacent("_baz"),
|
||||
"_baz": report.MakeNode("_baz").WithAdjacent("_foo"),
|
||||
}
|
||||
have := mapper.Render(report.MakeReport(), FilterNoop).Nodes
|
||||
have := mapper.Render(report.MakeReport()).Nodes
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Error(test.Diff(want, have))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
type TopologySelector string
|
||||
|
||||
// Render implements Renderer
|
||||
func (t TopologySelector) Render(r report.Report, _ Decorator) Nodes {
|
||||
func (t TopologySelector) Render(r report.Report) Nodes {
|
||||
topology, _ := r.Topology(string(t))
|
||||
return Nodes{Nodes: topology.Nodes}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ var (
|
||||
)
|
||||
|
||||
func TestShortLivedInternetNodeConnections(t *testing.T) {
|
||||
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(rpt, FilterNoop).Nodes)
|
||||
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(rpt).Nodes)
|
||||
|
||||
// Conntracked-only connections from the internet should be assigned to the internet pseudonode
|
||||
internet, ok := have[render.IncomingInternetID]
|
||||
@@ -123,7 +123,7 @@ func TestShortLivedInternetNodeConnections(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPauseContainerDiscarded(t *testing.T) {
|
||||
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(rpt, FilterNoop).Nodes)
|
||||
have := utils.Prune(render.ContainerWithImageNameRenderer.Render(rpt).Nodes)
|
||||
// There should only be a connection from container1 and the destination should be container2
|
||||
container1, ok := have[container1NodeID]
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user