Review Feedback

Squash of:
- including children in topologies_test.go
- report.Node.Prune should prune children also
- rewrote ShortLivedInternetConnections test to express its intent
- adding tests for detail Summary rendering
This commit is contained in:
Paul Bellamy
2016-03-24 13:43:19 +00:00
parent 2c6b6e6707
commit fe6203fd3f
18 changed files with 552 additions and 234 deletions

View File

@@ -10,10 +10,11 @@ import (
"os"
"os/exec"
"github.com/weaveworks/scope/render/detailed"
"github.com/weaveworks/scope/report"
)
func dot(w io.Writer, t report.Nodes) {
func dot(w io.Writer, t detailed.NodeSummaries) {
fmt.Fprintf(w, "digraph G {\n")
fmt.Fprintf(w, "\toutputorder=edgesfirst;\n")
fmt.Fprintf(w, "\toverlap=scale;\n")

View File

@@ -4,10 +4,11 @@ import (
"fmt"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/render/detailed"
"github.com/weaveworks/scope/report"
)
func renderTo(rpt report.Report, topology string) (report.Nodes, error) {
func renderTo(rpt report.Report, topology string) (detailed.NodeSummaries, error) {
renderer, ok := map[string]render.Renderer{
"processes": render.FilterUnconnected(render.ProcessWithContainerNameRenderer),
"processes-by-name": render.FilterUnconnected(render.ProcessNameRenderer),
@@ -16,7 +17,7 @@ func renderTo(rpt report.Report, topology string) (report.Nodes, error) {
"hosts": render.HostRenderer,
}[topology]
if !ok {
return report.Nodes{}, fmt.Errorf("unknown topology %v", topology)
return detailed.NodeSummaries{}, fmt.Errorf("unknown topology %v", topology)
}
return renderer.Render(rpt), nil
return detailed.Summaries(renderer.Render(rpt)), nil
}

View File

@@ -141,7 +141,7 @@ func (m *MetricRow) CodecDecodeSelf(decoder *codec.Decoder) {
}
// NodeMetrics produces a table (to be consumed directly by the UI) based on
// an origin ID, which is (optimistically) a node ID in one of our topologies.
// an a report.Node, which is (hopefully) a node in one of our topologies.
func NodeMetrics(n report.Node) []MetricRow {
if _, ok := n.Counters.Lookup(n.Topology); ok {
// This is a group of nodes, so no metrics!

View File

@@ -181,11 +181,14 @@ var (
func children(n report.Node) []NodeSummaryGroup {
summaries := map[string][]NodeSummary{}
n.Children.ForEach(func(child report.Node) {
if child.ID != n.ID {
if summary, ok := MakeNodeSummary(child); ok {
summaries[child.Topology] = append(summaries[child.Topology], summary.SummarizeMetrics())
}
if child.ID == n.ID {
return
}
summary, ok := MakeNodeSummary(child)
if !ok {
return
}
summaries[child.Topology] = append(summaries[child.Topology], summary.SummarizeMetrics())
})
nodeSummaryGroups := []NodeSummaryGroup{}

View File

@@ -182,7 +182,7 @@ func TestMakeDetailedContainerNode(t *testing.T) {
{ID: "docker_image_id", Value: fixture.ServerContainerImageID},
},
DockerLabels: []detailed.MetadataRow{
{ID: "label_" + render.AmazonECSContainerNameLabel, Value: `server`},
{ID: "label_" + detailed.AmazonECSContainerNameLabel, Value: `server`},
{ID: "label_foo1", Value: `bar1`},
{ID: "label_foo2", Value: `bar2`},
{ID: "label_io.kubernetes.pod.name", Value: "ping/pong-b"},

View File

@@ -6,7 +6,6 @@ import (
"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/probe/host"
"github.com/weaveworks/scope/probe/kubernetes"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
)
@@ -55,7 +54,7 @@ func Parents(r report.Report, n report.Node) (result []Parent) {
}
func containerParent(n report.Node) Parent {
label, _ := render.GetRenderableContainerName(n)
label := getRenderableContainerName(n)
containerID, _ := n.Latest.Lookup(docker.ContainerID)
return Parent{
ID: report.MakeContainerNodeID(containerID),

View File

@@ -20,6 +20,12 @@ const (
Heptagon = "heptagon"
Hexagon = "hexagon"
Cloud = "cloud"
ImageNameNone = "<none>"
// Keys we use to render container names
AmazonECSContainerNameLabel = "com.amazonaws.ecs.container-name"
KubernetesContainerNameLabel = "io.kubernetes.container.name"
)
// NodeSummaryGroup is a topology-typed group of children for a Node.
@@ -88,9 +94,7 @@ func MakeNodeSummary(n report.Node) (NodeSummary, bool) {
report.Host: hostNodeSummary,
}
if renderer, ok := renderers[n.Topology]; ok {
if summary, ok := baseNodeSummary(NodeSummary{}, n); ok {
return renderer(summary, n)
}
return renderer(baseNodeSummary(n), n)
}
return NodeSummary{}, false
}
@@ -129,15 +133,16 @@ func (n NodeSummary) Copy() NodeSummary {
return result
}
func baseNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
base.ID = n.ID
base.Shape = Circle
base.Linkable = true
base.Metadata = NodeMetadata(n)
base.DockerLabels = NodeDockerLabels(n)
base.Metrics = NodeMetrics(n)
base.Adjacency = n.Adjacency.Copy()
return base, true
func baseNodeSummary(n report.Node) NodeSummary {
return NodeSummary{
ID: n.ID,
Shape: Circle,
Linkable: true,
Metadata: NodeMetadata(n),
DockerLabels: NodeDockerLabels(n),
Metrics: NodeMetrics(n),
Adjacency: n.Adjacency.Copy(),
}
}
func pseudoNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
@@ -158,7 +163,8 @@ func pseudoNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
// try rendering it as an uncontained node
if strings.HasPrefix(n.ID, render.MakePseudoNodeID(render.UncontainedID)) {
base.Label = render.UncontainedMajor
base.Shape = Circle
base.Shape = Square
base.Stack = true
base.LabelMinor = report.ExtractHostID(n)
return base, true
}
@@ -202,7 +208,7 @@ func processNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
}
func containerNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
base.Label, _ = render.GetRenderableContainerName(n)
base.Label = getRenderableContainerName(n)
if c, ok := n.Counters.Lookup(report.Container); ok {
base.Stack = true
@@ -235,7 +241,7 @@ func containerImageNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bo
base.Shape = Hexagon
base.Stack = true
if base.Label == "<none>" {
if base.Label == ImageNameNone {
base.Label, _ = n.Latest.Lookup(docker.ImageID)
if len(base.Label) > 12 {
base.Label = base.Label[:12]
@@ -356,3 +362,28 @@ func (n NodeSummaries) Copy() NodeSummaries {
}
return result
}
// getRenderableContainerName obtains a user-friendly container name, to render in the UI
func getRenderableContainerName(nmd report.Node) string {
for _, key := range []string{
// Amazon's ecs-agent produces huge Docker container names, destructively
// derived from mangling Container Definition names in Task
// Definitions.
//
// However, the ecs-agent provides a label containing the original Container
// Definition name.
docker.LabelPrefix + AmazonECSContainerNameLabel,
// Kubernetes also mangles its Docker container names and provides a
// label with the original container name. However, note that this label
// is only provided by Kubernetes versions >= 1.2 (see
// https://github.com/kubernetes/kubernetes/pull/17234/ )
docker.LabelPrefix + KubernetesContainerNameLabel,
docker.ContainerName,
docker.ContainerHostname,
} {
if label, ok := nmd.Latest.Lookup(key); ok {
return label
}
}
return ""
}

View File

@@ -0,0 +1,186 @@
package detailed_test
import (
"sort"
"testing"
"time"
"github.com/weaveworks/scope/common/mtime"
"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/probe/host"
"github.com/weaveworks/scope/probe/process"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/render/detailed"
"github.com/weaveworks/scope/render/expected"
"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/test"
"github.com/weaveworks/scope/test/fixture"
"github.com/weaveworks/scope/test/reflect"
)
func TestSummaries(t *testing.T) {
{
// Just a convenient source of some rendered nodes
have := detailed.Summaries(render.ProcessRenderer.Render(fixture.Report))
// The ids of the processes rendered above
expectedIDs := []string{
fixture.ClientProcess1NodeID,
fixture.ClientProcess2NodeID,
fixture.ServerProcessNodeID,
fixture.NonContainerProcessNodeID,
expected.UnknownPseudoNode1ID,
expected.UnknownPseudoNode2ID,
render.IncomingInternetID,
render.OutgoingInternetID,
}
sort.Strings(expectedIDs)
// It should summarize each node
ids := []string{}
for id := range have {
ids = append(ids, id)
}
sort.Strings(ids)
if !reflect.DeepEqual(expectedIDs, ids) {
t.Fatalf("Expected Summaries to have summarized every node in the process renderer: %v, but got %v", expectedIDs, ids)
}
}
// It should summarize nodes' metrics
{
t1, t2 := mtime.Now().Add(-1*time.Minute), mtime.Now()
metric := report.MakeMetric().Add(t1, 1).Add(t2, 2)
input := fixture.Report.Copy()
input.Process.Nodes[fixture.ClientProcess1NodeID] = input.Process.Nodes[fixture.ClientProcess1NodeID].WithMetrics(report.Metrics{process.CPUUsage: metric})
have := detailed.Summaries(render.ProcessRenderer.Render(input))
node, ok := have[fixture.ClientProcess1NodeID]
if !ok {
t.Fatalf("Expected output to have the node we added the metric to")
}
var row detailed.MetricRow
ok = false
for _, metric := range node.Metrics {
if metric.ID == process.CPUUsage {
row = metric
ok = true
break
}
}
if !ok {
t.Fatalf("Expected node to have the metric we added")
}
// Our summarized MetricRow
want := detailed.MetricRow{
ID: process.CPUUsage,
Format: "percent",
Value: 2,
Metric: &report.Metric{
Samples: nil,
Min: metric.Min,
Max: metric.Max,
First: metric.First,
Last: metric.Last,
},
}
if !reflect.DeepEqual(want, row) {
t.Fatalf("Expected to have summarized the node's metrics: %s", test.Diff(want, row))
}
}
}
func TestMakeNodeSummary(t *testing.T) {
testcases := []struct {
name string
input report.Node
ok bool
want detailed.NodeSummary
}{
{
name: "single process rendering",
input: expected.RenderedProcesses[fixture.ClientProcess1NodeID],
ok: true,
want: detailed.NodeSummary{
ID: fixture.ClientProcess1NodeID,
Label: fixture.Client1Name,
LabelMinor: "client.hostname.com (10001)",
Rank: fixture.Client1Name,
Shape: "square",
Metadata: []detailed.MetadataRow{
{ID: process.PID, Value: fixture.Client1PID, Prime: true, Datatype: "number"},
},
Metrics: []detailed.MetricRow{},
Adjacency: report.MakeIDList(fixture.ServerProcessNodeID),
},
},
{
name: "single container rendering",
input: expected.RenderedContainers[fixture.ClientContainerNodeID],
ok: true,
want: detailed.NodeSummary{
ID: fixture.ClientContainerNodeID,
Label: fixture.ClientContainerName,
LabelMinor: fixture.ClientHostName,
Rank: fixture.ClientContainerImageName,
Shape: "hexagon",
Linkable: true,
Metadata: []detailed.MetadataRow{
{ID: docker.ContainerID, Value: fixture.ClientContainerID, Prime: true},
},
Metrics: []detailed.MetricRow{},
Adjacency: report.MakeIDList(fixture.ServerContainerNodeID),
},
},
{
name: "single container image rendering",
input: expected.RenderedContainerImages[fixture.ClientContainerImageNodeID],
ok: true,
want: detailed.NodeSummary{
ID: fixture.ClientContainerImageNodeID,
Label: fixture.ClientContainerImageName,
LabelMinor: "1 container",
Rank: fixture.ClientContainerImageName,
Shape: "hexagon",
Linkable: true,
Stack: true,
Metadata: []detailed.MetadataRow{
{ID: docker.ImageID, Value: fixture.ClientContainerImageID, Prime: true},
{ID: report.Container, Value: "1", Prime: true, Datatype: "number"},
},
Adjacency: report.MakeIDList(fixture.ServerContainerImageNodeID),
},
},
{
name: "single host rendering",
input: expected.RenderedHosts[fixture.ClientHostNodeID],
ok: true,
want: detailed.NodeSummary{
ID: fixture.ClientHostNodeID,
Label: "client",
LabelMinor: "hostname.com",
Rank: "hostname.com",
Shape: "circle",
Linkable: true,
Metadata: []detailed.MetadataRow{
{ID: host.HostName, Value: fixture.ClientHostName, Prime: false},
},
Metrics: []detailed.MetricRow{},
Adjacency: report.MakeIDList(fixture.ServerHostNodeID),
},
},
}
for _, testcase := range testcases {
have, ok := detailed.MakeNodeSummary(testcase.input)
if ok != testcase.ok {
t.Errorf("%s: MakeNodeSummary failed: expected ok value to be: %v", testcase.name, testcase.ok)
continue
}
if !reflect.DeepEqual(testcase.want, have) {
t.Errorf("%s: Node Summary did not match: %s", testcase.name, test.Diff(testcase.want, have))
}
}
}

View File

@@ -1,6 +1,9 @@
package expected
import (
"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/probe/host"
"github.com/weaveworks/scope/probe/process"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/test/fixture"
@@ -25,13 +28,41 @@ var (
}
}
pseudo = node(render.Pseudo)
endpoint = node("") // TODO: endpoints don't have a topology for some reason? Not being tagged?
process = node(report.Process)
endpoint = node(report.Endpoint)
processNode = node(report.Process)
container = node(report.Container)
containerImage = node(report.ContainerImage)
pod = node(report.Pod)
service = node(report.Service)
host = node(report.Host)
hostNode = node(report.Host)
UnknownPseudoNode1ID = render.MakePseudoNodeID(fixture.UnknownClient1IP)
UnknownPseudoNode2ID = render.MakePseudoNodeID(fixture.UnknownClient3IP)
unknownPseudoNode1 = func(adjacent ...string) report.Node {
return pseudo(UnknownPseudoNode1ID, adjacent...).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.UnknownClient1NodeID],
RenderedEndpoints[fixture.UnknownClient2NodeID],
))
}
unknownPseudoNode2 = func(adjacent ...string) report.Node {
return pseudo(UnknownPseudoNode2ID, adjacent...).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.UnknownClient3NodeID],
))
}
theIncomingInternetNode = func(adjacent ...string) report.Node {
return pseudo(render.IncomingInternetID, adjacent...).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.RandomClientNodeID],
))
}
theOutgoingInternetNode = pseudo(render.OutgoingInternetID).WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.GoogleEndpointNodeID],
))
RenderedEndpoints = report.Nodes{
fixture.Client54001NodeID: endpoint(fixture.Client54001NodeID, fixture.Server80NodeID),
@@ -43,81 +74,203 @@ var (
fixture.RandomClientNodeID: endpoint(fixture.RandomClientNodeID, fixture.Server80NodeID),
fixture.NonContainerNodeID: endpoint(fixture.NonContainerNodeID, fixture.GoogleEndpointNodeID),
fixture.GoogleEndpointNodeID: endpoint(fixture.GoogleEndpointNodeID),
}.Prune()
}
RenderedProcesses = report.Nodes{
fixture.ClientProcess1NodeID: process(fixture.ClientProcess1NodeID, fixture.ServerProcessNodeID),
fixture.ClientProcess2NodeID: process(fixture.ClientProcess2NodeID, fixture.ServerProcessNodeID),
fixture.ServerProcessNodeID: process(fixture.ServerProcessNodeID),
fixture.NonContainerProcessNodeID: process(fixture.NonContainerProcessNodeID, render.OutgoingInternetID),
unknownPseudoNode1ID: pseudo(unknownPseudoNode1ID, fixture.ServerProcessNodeID),
unknownPseudoNode2ID: pseudo(unknownPseudoNode2ID, fixture.ServerProcessNodeID),
render.IncomingInternetID: pseudo(render.IncomingInternetID, fixture.ServerProcessNodeID),
render.OutgoingInternetID: pseudo(render.OutgoingInternetID),
}.Prune()
fixture.ClientProcess1NodeID: processNode(fixture.ClientProcess1NodeID, fixture.ServerProcessNodeID).
WithLatests(map[string]string{
report.HostNodeID: fixture.ClientHostNodeID,
process.PID: fixture.Client1PID,
process.Name: fixture.Client1Name,
}).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Client54001NodeID],
)),
unknownPseudoNode1ID = render.MakePseudoNodeID(fixture.UnknownClient1IP)
unknownPseudoNode2ID = render.MakePseudoNodeID(fixture.UnknownClient3IP)
fixture.ClientProcess2NodeID: processNode(fixture.ClientProcess2NodeID, fixture.ServerProcessNodeID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Client54002NodeID],
)),
fixture.ServerProcessNodeID: processNode(fixture.ServerProcessNodeID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Server80NodeID],
)),
fixture.NonContainerProcessNodeID: processNode(fixture.NonContainerProcessNodeID, render.OutgoingInternetID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.NonContainerNodeID],
)),
UnknownPseudoNode1ID: unknownPseudoNode1(fixture.ServerProcessNodeID),
UnknownPseudoNode2ID: unknownPseudoNode2(fixture.ServerProcessNodeID),
render.IncomingInternetID: theIncomingInternetNode(fixture.ServerProcessNodeID),
render.OutgoingInternetID: theOutgoingInternetNode,
}
RenderedProcessNames = report.Nodes{
fixture.Client1Name: process(fixture.Client1Name, fixture.ServerName),
fixture.ServerName: process(fixture.ServerName),
fixture.NonContainerName: process(fixture.NonContainerName, render.OutgoingInternetID),
unknownPseudoNode1ID: pseudo(unknownPseudoNode1ID, fixture.ServerName),
unknownPseudoNode2ID: pseudo(unknownPseudoNode2ID, fixture.ServerName),
render.IncomingInternetID: pseudo(render.IncomingInternetID, fixture.ServerName),
render.OutgoingInternetID: pseudo(render.OutgoingInternetID),
}.Prune()
fixture.Client1Name: processNode(fixture.Client1Name, fixture.ServerName).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Client54001NodeID],
RenderedEndpoints[fixture.Client54002NodeID],
RenderedProcesses[fixture.ClientProcess1NodeID],
RenderedProcesses[fixture.ClientProcess2NodeID],
)),
uncontainedServerID = render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostID)
fixture.ServerName: processNode(fixture.ServerName).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Server80NodeID],
RenderedProcesses[fixture.ServerProcessNodeID],
)),
fixture.NonContainerName: processNode(fixture.NonContainerName, render.OutgoingInternetID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.NonContainerNodeID],
RenderedProcesses[fixture.NonContainerProcessNodeID],
)),
UnknownPseudoNode1ID: unknownPseudoNode1(fixture.ServerName),
UnknownPseudoNode2ID: unknownPseudoNode2(fixture.ServerName),
render.IncomingInternetID: theIncomingInternetNode(fixture.ServerName),
render.OutgoingInternetID: theOutgoingInternetNode,
}
uncontainedServerID = render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostID)
uncontainedServerNode = pseudo(uncontainedServerID, render.OutgoingInternetID).WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.NonContainerNodeID],
RenderedProcesses[fixture.NonContainerProcessNodeID],
))
RenderedContainers = report.Nodes{
fixture.ClientContainerNodeID: container(fixture.ClientContainerNodeID, fixture.ServerContainerNodeID),
fixture.ServerContainerNodeID: container(fixture.ServerContainerNodeID),
uncontainedServerID: pseudo(uncontainedServerID, render.OutgoingInternetID),
// unknownPseudoNode1ID: pseudo(unknownPseudoNode1ID, fixture.ServerContainerNodeID),
// unknownPseudoNode2ID: pseudo(unknownPseudoNode2ID, fixture.ServerContainerNodeID),
render.IncomingInternetID: pseudo(render.IncomingInternetID, fixture.ServerContainerNodeID),
render.OutgoingInternetID: pseudo(render.OutgoingInternetID),
}.Prune()
fixture.ClientContainerNodeID: container(fixture.ClientContainerNodeID, fixture.ServerContainerNodeID).
WithLatests(map[string]string{
report.HostNodeID: fixture.ClientHostNodeID,
docker.ContainerID: fixture.ClientContainerID,
docker.ContainerName: fixture.ClientContainerName,
docker.ImageName: fixture.ClientContainerImageName,
}).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Client54001NodeID],
RenderedEndpoints[fixture.Client54002NodeID],
RenderedProcesses[fixture.ClientProcess1NodeID],
RenderedProcesses[fixture.ClientProcess2NodeID],
)),
fixture.ServerContainerNodeID: container(fixture.ServerContainerNodeID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Server80NodeID],
RenderedProcesses[fixture.ServerProcessNodeID],
)),
uncontainedServerID: uncontainedServerNode,
render.IncomingInternetID: theIncomingInternetNode(fixture.ServerContainerNodeID),
render.OutgoingInternetID: theOutgoingInternetNode,
}
RenderedContainerImages = report.Nodes{
fixture.ClientContainerImageNodeID: containerImage(fixture.ClientContainerImageNodeID, fixture.ServerContainerImageNodeID),
fixture.ServerContainerImageNodeID: containerImage(fixture.ServerContainerImageNodeID),
uncontainedServerID: pseudo(uncontainedServerID, render.OutgoingInternetID),
// unknownPseudoNode1ID: pseudo(unknownPseudoNode1ID, fixture.ServerContainerImageNodeID),
// unknownPseudoNode2ID: pseudo(unknownPseudoNode2ID, fixture.ServerContainerImageNodeID),
render.IncomingInternetID: pseudo(render.IncomingInternetID, fixture.ServerContainerImageNodeID),
render.OutgoingInternetID: pseudo(render.OutgoingInternetID),
}.Prune()
fixture.ClientContainerImageNodeID: containerImage(fixture.ClientContainerImageNodeID, fixture.ServerContainerImageNodeID).
WithLatests(map[string]string{
report.HostNodeID: fixture.ClientHostNodeID,
docker.ImageID: fixture.ClientContainerImageID,
docker.ImageName: fixture.ClientContainerImageName,
}).
WithCounters(map[string]int{
report.Container: 1,
}).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Client54001NodeID],
RenderedEndpoints[fixture.Client54002NodeID],
RenderedProcesses[fixture.ClientProcess1NodeID],
RenderedProcesses[fixture.ClientProcess2NodeID],
RenderedContainers[fixture.ClientContainerNodeID],
)),
RenderedPods = report.Nodes{
fixture.ClientPodNodeID: pod(fixture.ClientPodNodeID, fixture.ServerPodNodeID),
fixture.ServerPodNodeID: pod(fixture.ServerPodNodeID),
uncontainedServerID: pseudo(uncontainedServerID, render.OutgoingInternetID),
// unknownPseudoNode1ID: pseudo(unknownPseudoNode1ID, fixture.ServerPodNodeID),
// unknownPseudoNode2ID: pseudo(unknownPseudoNode2ID, fixture.ServerPodNodeID),
render.IncomingInternetID: pseudo(render.IncomingInternetID, fixture.ServerPodNodeID),
render.OutgoingInternetID: pseudo(render.OutgoingInternetID),
}.Prune()
fixture.ServerContainerImageNodeID: containerImage(fixture.ServerContainerImageNodeID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Server80NodeID],
RenderedProcesses[fixture.ServerProcessNodeID],
RenderedContainers[fixture.ServerContainerNodeID],
)),
uncontainedServerID: uncontainedServerNode,
render.IncomingInternetID: theIncomingInternetNode(fixture.ServerContainerImageNodeID),
render.OutgoingInternetID: theOutgoingInternetNode,
}
RenderedHosts = report.Nodes{
fixture.ClientHostNodeID: host(fixture.ClientHostNodeID, fixture.ServerHostNodeID),
fixture.ServerHostNodeID: host(fixture.ServerHostNodeID, render.OutgoingInternetID),
unknownPseudoNode1ID: pseudo(unknownPseudoNode1ID, fixture.ServerHostNodeID),
unknownPseudoNode2ID: pseudo(unknownPseudoNode2ID, fixture.ServerHostNodeID),
render.IncomingInternetID: pseudo(render.IncomingInternetID, fixture.ServerHostNodeID),
render.OutgoingInternetID: pseudo(render.OutgoingInternetID),
}.Prune()
fixture.ClientHostNodeID: hostNode(fixture.ClientHostNodeID, fixture.ServerHostNodeID).
WithLatests(map[string]string{
host.HostName: fixture.ClientHostName,
}).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Client54001NodeID],
RenderedEndpoints[fixture.Client54002NodeID],
RenderedProcesses[fixture.ClientProcess1NodeID],
RenderedProcesses[fixture.ClientProcess2NodeID],
RenderedContainers[fixture.ClientContainerNodeID],
RenderedContainerImages[fixture.ClientContainerImageNodeID],
//RenderedPods[fixture.ClientPodNodeID], #1142
)),
fixture.ServerHostNodeID: hostNode(fixture.ServerHostNodeID, render.OutgoingInternetID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Server80NodeID],
RenderedEndpoints[fixture.NonContainerNodeID],
RenderedProcesses[fixture.ServerProcessNodeID],
RenderedProcesses[fixture.NonContainerProcessNodeID],
RenderedContainers[fixture.ServerContainerNodeID],
RenderedContainerImages[fixture.ServerContainerImageNodeID],
//RenderedPods[fixture.ServerPodNodeID], #1142
)),
UnknownPseudoNode1ID: unknownPseudoNode1(fixture.ServerHostNodeID),
UnknownPseudoNode2ID: unknownPseudoNode2(fixture.ServerHostNodeID),
render.IncomingInternetID: theIncomingInternetNode(fixture.ServerHostNodeID),
render.OutgoingInternetID: theOutgoingInternetNode,
}
RenderedPods = report.Nodes{
fixture.ClientPodNodeID: pod(fixture.ClientPodNodeID, fixture.ServerPodNodeID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Client54001NodeID],
RenderedEndpoints[fixture.Client54002NodeID],
RenderedProcesses[fixture.ClientProcess1NodeID],
RenderedProcesses[fixture.ClientProcess2NodeID],
RenderedContainers[fixture.ClientContainerNodeID],
)),
fixture.ServerPodNodeID: pod(fixture.ServerPodNodeID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Server80NodeID],
RenderedProcesses[fixture.ServerProcessNodeID],
RenderedContainers[fixture.ServerContainerNodeID],
)),
uncontainedServerID: uncontainedServerNode,
render.IncomingInternetID: theIncomingInternetNode(fixture.ServerPodNodeID),
render.OutgoingInternetID: theOutgoingInternetNode,
}
RenderedPodServices = report.Nodes{
fixture.ServiceNodeID: service(fixture.ServiceNodeID, fixture.ServiceNodeID),
uncontainedServerID: pseudo(uncontainedServerID, render.OutgoingInternetID),
// unknownPseudoNode1ID: pseudo(unknownPseudoNode1ID, fixture.ServiceID),
// unknownPseudoNode2ID: pseudo(unknownPseudoNode2ID, fixture.ServiceID),
render.IncomingInternetID: pseudo(render.IncomingInternetID, fixture.ServiceNodeID),
render.OutgoingInternetID: pseudo(render.OutgoingInternetID),
}.Prune()
fixture.ServiceNodeID: service(fixture.ServiceNodeID, fixture.ServiceNodeID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Client54001NodeID],
RenderedEndpoints[fixture.Client54002NodeID],
RenderedEndpoints[fixture.Server80NodeID],
RenderedProcesses[fixture.ClientProcess1NodeID],
RenderedProcesses[fixture.ClientProcess2NodeID],
RenderedProcesses[fixture.ServerProcessNodeID],
RenderedContainers[fixture.ClientContainerNodeID],
RenderedContainers[fixture.ServerContainerNodeID],
RenderedPods[fixture.ClientPodNodeID],
RenderedPods[fixture.ServerPodNodeID],
)),
uncontainedServerID: uncontainedServerNode,
render.IncomingInternetID: theIncomingInternetNode(fixture.ServiceNodeID),
render.OutgoingInternetID: theOutgoingInternetNode,
}
)
func newu64(value uint64) *uint64 { return &value }

View File

@@ -3,6 +3,7 @@ package render
import (
"strings"
"github.com/weaveworks/scope/common/mtime"
"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/probe/endpoint"
"github.com/weaveworks/scope/probe/kubernetes"
@@ -47,7 +48,7 @@ func ColorConnected(r Renderer) Renderer {
output := input.Copy()
for id := range connected {
output[id] = output[id].WithLatests(map[string]string{IsConnected: "true"})
output[id] = output[id].WithLatest(IsConnected, mtime.Now(), "true")
}
return output
},

View File

@@ -28,11 +28,9 @@ const (
ipsKey = "ips"
AmazonECSContainerNameLabel = "com.amazonaws.ecs.container-name"
KubernetesContainerNameLabel = "io.kubernetes.container.name"
// Topology for pseudo-nodes so we can differentiate them at the end
// Topology for pseudo-nodes and IPs so we can differentiate them at the end
Pseudo = "pseudo"
IP = "IP"
)
// MapFunc is anything which can take an arbitrary Node and
@@ -59,31 +57,6 @@ func theInternetNode(m report.Node) report.Node {
return NewDerivedPseudoNode(OutgoingInternetID, m)
}
// GetRenderableContainerName obtains a user-friendly container name, to render in the UI
func GetRenderableContainerName(nmd report.Node) (string, bool) {
for _, key := range []string{
// Amazon's ecs-agent produces huge Docker container names, destructively
// derived from mangling Container Definition names in Task
// Definitions.
//
// However, the ecs-agent provides a label containing the original Container
// Definition name.
docker.LabelPrefix + AmazonECSContainerNameLabel,
// Kubernetes also mangles its Docker container names and provides a
// label with the original container name. However, note that this label
// is only provided by Kubernetes versions >= 1.2 (see
// https://github.com/kubernetes/kubernetes/pull/17234/ )
docker.LabelPrefix + KubernetesContainerNameLabel,
docker.ContainerName,
docker.ContainerHostname,
} {
if label, ok := nmd.Latest.Lookup(key); ok {
return label, true
}
}
return "", false
}
// MapEndpoint2IP maps endpoint nodes to their IP address, for joining
// with container nodes. We drop endpoint nodes with pids, as they
// will be joined to containers through the process topology, and we
@@ -110,8 +83,8 @@ func MapEndpoint2IP(m report.Node, local report.Networks) report.Nodes {
id := report.MakeScopedEndpointNodeID(scope, addr, "")
idWithPort := report.MakeScopedEndpointNodeID(scope, addr, port)
return report.Nodes{
id: NewDerivedNode(id, m).WithTopology("IP"),
idWithPort: NewDerivedNode(idWithPort, m).WithTopology("IP"),
id: NewDerivedNode(id, m).WithTopology(IP),
idWithPort: NewDerivedNode(idWithPort, m).WithTopology(IP),
}
}
@@ -135,7 +108,7 @@ func MapContainer2IP(m report.Node, _ report.Networks) report.Nodes {
}
id := report.MakeScopedEndpointNodeID(scope, addr, "")
result[id] = NewDerivedNode(id, m).
WithTopology("IP").
WithTopology(IP).
WithLatests(map[string]string{docker.ContainerID: containerID}).
WithCounters(map[string]int{ipsKey: 1})
@@ -150,7 +123,7 @@ func MapContainer2IP(m report.Node, _ report.Networks) report.Nodes {
ip, port := mapping[1], mapping[2]
id := report.MakeScopedEndpointNodeID("", ip, port)
result[id] = NewDerivedNode(id, m).
WithTopology("IP").
WithTopology(IP).
WithLatests(map[string]string{docker.ContainerID: containerID}).
WithCounters(map[string]int{ipsKey: 1})
@@ -226,43 +199,18 @@ func MapEndpoint2Process(n report.Node, local report.Networks) report.Nodes {
return MapEndpoint2Pseudo(n, local)
}
pid, ok := n.Latest.LookupEntry(process.PID)
pid, timestamp, ok := n.Latest.LookupEntry(process.PID)
if !ok {
return report.Nodes{}
}
id := report.MakeProcessNodeID(report.ExtractHostID(n), pid.Value)
id := report.MakeProcessNodeID(report.ExtractHostID(n), pid)
node := NewDerivedNode(id, n).WithTopology(report.Process)
node.Latest = node.Latest.Set(process.PID, pid.Timestamp, pid.Value)
node.Latest = node.Latest.Set(process.PID, timestamp, pid)
node.Counters = node.Counters.Add(n.Topology, 1)
return report.Nodes{id: node}
}
// MapTo re-indexes a set of nodes by some key from Latest, changing the topology
// TODO(paulbellamy): not sure this works, or is general enough
func MapTo(topology, key string) func(report.Node, report.Networks) report.Nodes {
return func(n report.Node, local report.Networks) report.Nodes {
// Pass through pseudo nodes
if n.Topology == Pseudo {
fmt.Printf("[DEBUG] MapTo skipping %s %s", n.Topology, n.ID)
return report.Nodes{n.ID: n}
}
// Throw away any nodes not having this key
entry, ok := n.Latest.LookupEntry(key)
if !ok {
fmt.Printf("[DEBUG] MapTo skipping %s %s: no key %s", n.Topology, n.ID, key)
return report.Nodes{}
}
node := NewDerivedNode(entry.Value, n).WithTopology(topology)
node.Latest = node.Latest.Set(key, entry.Timestamp, entry.Value)
node.Counters = node.Counters.Add(n.Topology, 1)
fmt.Printf("[DEBUG] MapTo %s %s -> %s %s", n.Topology, n.ID, node.Topology, node.ID)
return report.Nodes{entry.Value: node}
}
}
// MapProcess2Container maps process Nodes to container
// Nodes.
//
@@ -313,15 +261,15 @@ func MapProcess2Name(n report.Node, _ report.Networks) report.Nodes {
return report.Nodes{n.ID: n}
}
name, ok := n.Latest.LookupEntry(process.Name)
name, timestamp, ok := n.Latest.LookupEntry(process.Name)
if !ok {
return report.Nodes{}
}
node := NewDerivedNode(name.Value, n).WithTopology(report.Process)
node.Latest = node.Latest.Set(process.Name, name.Timestamp, name.Value)
node := NewDerivedNode(name, n).WithTopology(report.Process)
node.Latest = node.Latest.Set(process.Name, timestamp, name)
node.Counters = node.Counters.Add(n.Topology, 1)
return report.Nodes{name.Value: node}
return report.Nodes{name: node}
}
// MapContainer2ContainerImage maps container Nodes to container
@@ -343,15 +291,15 @@ func MapContainer2ContainerImage(n report.Node, _ report.Networks) report.Nodes
// Otherwise, if some some reason the container doesn't have a image_id
// (maybe slightly out of sync reports), just drop it
imageID, ok := n.Latest.LookupEntry(docker.ImageID)
imageID, timestamp, ok := n.Latest.LookupEntry(docker.ImageID)
if !ok {
return report.Nodes{}
}
// Add container id key to the counters, which will later be counted to produce the minor label
id := report.MakeContainerImageNodeID(imageID.Value)
id := report.MakeContainerImageNodeID(imageID)
result := NewDerivedNode(id, n).WithTopology(report.ContainerImage)
result.Latest = result.Latest.Set(docker.ImageID, imageID.Timestamp, imageID.Value)
result.Latest = result.Latest.Set(docker.ImageID, timestamp, imageID)
result.Counters = result.Counters.Add(n.Topology, 1)
return report.Nodes{id: result}
}
@@ -382,13 +330,13 @@ func MapX2Host(n report.Node, _ report.Networks) report.Nodes {
if n.Topology == Pseudo {
return report.Nodes{}
}
hostNodeID, ok := n.Latest.LookupEntry(report.HostNodeID)
hostNodeID, timestamp, ok := n.Latest.LookupEntry(report.HostNodeID)
if !ok {
return report.Nodes{}
}
id := report.MakeHostNodeID(report.ExtractHostID(n))
result := NewDerivedNode(id, n).WithTopology(report.Host)
result.Latest = result.Latest.Set(report.HostNodeID, hostNodeID.Timestamp, hostNodeID.Value)
result.Latest = result.Latest.Set(report.HostNodeID, timestamp, hostNodeID)
result.Counters = result.Counters.Add(n.Topology, 1)
return report.Nodes{id: result}
}
@@ -397,14 +345,14 @@ func MapX2Host(n report.Node, _ report.Networks) report.Nodes {
// host nodes or pseudo nodes.
func MapEndpoint2Host(n report.Node, local report.Networks) report.Nodes {
// Nodes without a hostid are treated as pseudo nodes
hostNodeID, ok := n.Latest.LookupEntry(report.HostNodeID)
hostNodeID, timestamp, ok := n.Latest.LookupEntry(report.HostNodeID)
if !ok {
return MapEndpoint2Pseudo(n, local)
}
id := report.MakeHostNodeID(report.ExtractHostID(n))
result := NewDerivedNode(id, n).WithTopology(report.Host)
result.Latest = result.Latest.Set(report.HostNodeID, hostNodeID.Timestamp, hostNodeID.Value)
result.Latest = result.Latest.Set(report.HostNodeID, timestamp, hostNodeID)
result.Counters = result.Counters.Add(n.Topology, 1)
return report.Nodes{id: result}
}
@@ -497,15 +445,15 @@ func MapContainer2Hostname(n report.Node, _ report.Networks) report.Nodes {
// Otherwise, if some some reason the container doesn't have a hostname
// (maybe slightly out of sync reports), just drop it
id, ok := n.Latest.LookupEntry(docker.ContainerHostname)
id, timestamp, ok := n.Latest.LookupEntry(docker.ContainerHostname)
if !ok {
return report.Nodes{}
}
node := NewDerivedNode(id.Value, n)
node := NewDerivedNode(id, n)
node.Latest = node.Latest.
Set(docker.ContainerHostname, id.Timestamp, id.Value).
Set(docker.ContainerHostname, timestamp, id).
Delete(docker.ContainerName) // TODO(paulbellamy): total hack to render these by hostname instead.
node.Counters = node.Counters.Add(n.Topology, 1)
return report.Nodes{id.Value: node}
return report.Nodes{id: node}
}

View File

@@ -2,7 +2,6 @@ package render_test
import (
"fmt"
"reflect"
"testing"
"github.com/weaveworks/scope/probe/docker"
@@ -10,7 +9,6 @@ import (
"github.com/weaveworks/scope/probe/host"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/test"
)
var (
@@ -68,21 +66,18 @@ var (
},
},
}
want = (report.Nodes{
render.IncomingInternetID: report.MakeNode().
WithID(render.IncomingInternetID).
WithTopology(render.Pseudo).
WithAdjacent(report.MakeContainerNodeID(containerID)),
report.MakeContainerNodeID(containerID): report.MakeNode().
WithID(report.MakeContainerNodeID(containerID)).
WithTopology(report.Container),
})
)
func TestShortLivedInternetNodeConnections(t *testing.T) {
have := render.ContainerWithImageNameRenderer.Render(rpt).Prune()
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
// Conntracked-only connections from the internet should be assigned to the internet pseudonode
internet, ok := have[render.IncomingInternetID]
if !ok {
t.Fatal("Expected output to have an incoming internet node")
}
if !internet.Adjacency.Contains(report.MakeContainerNodeID(containerID)) {
t.Errorf("Expected internet node to have adjacency to %s, but only had %v", report.MakeContainerNodeID(containerID), internet.Adjacency)
}
}

View File

@@ -34,18 +34,18 @@ func (r processWithContainerNameRenderer) Render(rpt report.Report) report.Nodes
outputs := report.Nodes{}
for id, p := range processes {
outputs[id] = p
containerIDEntry, ok := p.Latest.LookupEntry(docker.ContainerID)
containerID, timestamp, ok := p.Latest.LookupEntry(docker.ContainerID)
if !ok {
continue
}
container, ok := containers[report.MakeContainerNodeID(containerIDEntry.Value)]
container, ok := containers[report.MakeContainerNodeID(containerID)]
if !ok {
continue
}
output := p.Copy()
output.Latest = output.Latest.Set(docker.ContainerID, containerIDEntry.Timestamp, containerIDEntry.Value)
if entry, ok := container.Latest.LookupEntry(docker.ContainerName); ok {
output.Latest = output.Latest.Set(docker.ContainerName, entry.Timestamp, entry.Value)
output.Latest = output.Latest.Set(docker.ContainerID, timestamp, containerID)
if containerName, timestamp, ok := container.Latest.LookupEntry(docker.ContainerName); ok {
output.Latest = output.Latest.Set(docker.ContainerName, timestamp, containerName)
}
outputs[id] = output
}

View File

@@ -15,7 +15,7 @@ import (
func TestEndpointRenderer(t *testing.T) {
have := render.EndpointRenderer.Render(fixture.Report).Prune()
want := expected.RenderedEndpoints
want := expected.RenderedEndpoints.Prune()
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -23,7 +23,7 @@ func TestEndpointRenderer(t *testing.T) {
func TestProcessRenderer(t *testing.T) {
have := render.ProcessRenderer.Render(fixture.Report).Prune()
want := expected.RenderedProcesses
want := expected.RenderedProcesses.Prune()
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -31,7 +31,7 @@ func TestProcessRenderer(t *testing.T) {
func TestProcessNameRenderer(t *testing.T) {
have := render.ProcessNameRenderer.Render(fixture.Report).Prune()
want := expected.RenderedProcessNames
want := expected.RenderedProcessNames.Prune()
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -39,7 +39,7 @@ func TestProcessNameRenderer(t *testing.T) {
func TestContainerRenderer(t *testing.T) {
have := render.ContainerRenderer.Render(fixture.Report).Prune()
want := expected.RenderedContainers
want := expected.RenderedContainers.Prune()
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -53,7 +53,7 @@ func TestContainerFilterRenderer(t *testing.T) {
docker.LabelPrefix + "works.weave.role": "system",
})
have := render.FilterSystem(render.ContainerRenderer).Render(input).Prune()
want := expected.RenderedContainers.Copy()
want := expected.RenderedContainers.Copy().Prune()
delete(want, fixture.ClientContainerNodeID)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
@@ -84,7 +84,7 @@ func TestContainerWithHostIPsRenderer(t *testing.T) {
func TestContainerImageRenderer(t *testing.T) {
have := render.ContainerImageRenderer.Render(fixture.Report).Prune()
want := expected.RenderedContainerImages
want := expected.RenderedContainerImages.Prune()
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -92,7 +92,7 @@ func TestContainerImageRenderer(t *testing.T) {
func TestHostRenderer(t *testing.T) {
have := render.HostRenderer.Render(fixture.Report).Prune()
want := expected.RenderedHosts
want := expected.RenderedHosts.Prune()
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -100,7 +100,7 @@ func TestHostRenderer(t *testing.T) {
func TestPodRenderer(t *testing.T) {
have := render.PodRenderer.Render(fixture.Report).Prune()
want := expected.RenderedPods
want := expected.RenderedPods.Prune()
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
@@ -119,7 +119,7 @@ func TestPodFilterRenderer(t *testing.T) {
docker.LabelPrefix + "io.kubernetes.pod.name": "kube-system/foo",
})
have := render.FilterSystem(render.PodRenderer).Render(input).Prune()
want := expected.RenderedPods.Copy()
want := expected.RenderedPods.Copy().Prune()
delete(want, fixture.ClientPodNodeID)
delete(want, fixture.ClientContainerNodeID)
if !reflect.DeepEqual(want, have) {
@@ -129,7 +129,7 @@ func TestPodFilterRenderer(t *testing.T) {
func TestPodServiceRenderer(t *testing.T) {
have := render.PodServiceRenderer.Render(fixture.Report).Prune()
want := expected.RenderedPodServices
want := expected.RenderedPodServices.Prune()
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}

View File

@@ -86,26 +86,21 @@ func (m LatestMap) Merge(other LatestMap) LatestMap {
// Lookup the value for the given key.
func (m LatestMap) Lookup(key string) (string, bool) {
if m.Map == nil {
return "", false
}
value, ok := m.Map.Lookup(key)
if !ok {
return "", false
}
return value.(LatestEntry).Value, true
v, _, ok := m.LookupEntry(key)
return v, ok
}
// LookupEntry returns the raw entry for the given key.
func (m LatestMap) LookupEntry(key string) (LatestEntry, bool) {
func (m LatestMap) LookupEntry(key string) (string, time.Time, bool) {
if m.Map == nil {
return LatestEntry{}, false
return "", time.Time{}, false
}
value, ok := m.Map.Lookup(key)
if !ok {
return LatestEntry{}, false
return "", time.Time{}, false
}
return value.(LatestEntry), true
e := value.(LatestEntry)
return e.Value, e.Timestamp, true
}
// Set the value for the given key.

View File

@@ -33,11 +33,11 @@ func TestLatestMapLookupEntry(t *testing.T) {
now := time.Now()
entry := LatestEntry{Timestamp: now, Value: "Bar"}
have := EmptyLatestMap.Set("foo", entry.Timestamp, entry.Value)
if got, ok := have.LookupEntry("foo"); !ok || got != entry {
t.Errorf("got: %#v != expected %#v", got, entry)
if got, timestamp, ok := have.LookupEntry("foo"); !ok || got != entry.Value || !timestamp.Equal(entry.Timestamp) {
t.Errorf("got: %#v %v != expected %#v", got, timestamp, entry)
}
if got, ok := have.LookupEntry("not found"); ok {
t.Errorf("found unexpected entry for %q: %#v", "not found", got)
if got, timestamp, ok := have.LookupEntry("not found"); ok {
t.Errorf("found unexpected entry for %q: %#v %v", "not found", got, timestamp)
}
}

View File

@@ -219,8 +219,13 @@ func (n Node) Merge(other Node) Node {
// for rendering nodes and edges stripped away. Specifically, that means
// cutting out parts of the Node.
func (n Node) Prune() Node {
prunedChildren := MakeNodeSet()
n.Children.ForEach(func(child Node) {
prunedChildren = prunedChildren.Add(child.Prune())
})
return MakeNode().
WithID(n.ID).
WithTopology(n.Topology).
WithAdjacent(n.Adjacency.Copy()...)
WithAdjacent(n.Adjacency.Copy()...).
WithChildren(prunedChildren)
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/weaveworks/scope/probe/host"
"github.com/weaveworks/scope/probe/kubernetes"
"github.com/weaveworks/scope/probe/process"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/render/detailed"
"github.com/weaveworks/scope/report"
)
@@ -121,7 +121,7 @@ var (
// Node is arbitrary. We're free to put only precisely what we
// care to test into the fixture. Just be sure to include the bits
// that the mapping funcs extract :)
Client54001NodeID: report.MakeNode().WithID(Client54001NodeID).WithLatests(map[string]string{
Client54001NodeID: report.MakeNode().WithID(Client54001NodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
endpoint.Addr: ClientIP,
endpoint.Port: ClientPort54001,
process.PID: Client1PID,
@@ -132,7 +132,7 @@ var (
EgressByteCount: newu64(100),
}),
Client54002NodeID: report.MakeNode().WithID(Client54002NodeID).WithLatests(map[string]string{
Client54002NodeID: report.MakeNode().WithID(Client54002NodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
endpoint.Addr: ClientIP,
endpoint.Port: ClientPort54002,
process.PID: Client2PID,
@@ -143,7 +143,7 @@ var (
EgressByteCount: newu64(200),
}),
Server80NodeID: report.MakeNode().WithID(Server80NodeID).WithLatests(map[string]string{
Server80NodeID: report.MakeNode().WithID(Server80NodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
endpoint.Addr: ServerIP,
endpoint.Port: ServerPort,
process.PID: ServerPID,
@@ -151,7 +151,7 @@ var (
endpoint.Procspied: True,
}),
NonContainerNodeID: report.MakeNode().WithID(NonContainerNodeID).WithLatests(map[string]string{
NonContainerNodeID: report.MakeNode().WithID(NonContainerNodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
endpoint.Addr: ServerIP,
endpoint.Port: NonContainerClientPort,
process.PID: NonContainerPID,
@@ -160,7 +160,7 @@ var (
}).WithAdjacent(GoogleEndpointNodeID),
// Probe pseudo nodes
UnknownClient1NodeID: report.MakeNode().WithID(UnknownClient1NodeID).WithLatests(map[string]string{
UnknownClient1NodeID: report.MakeNode().WithID(UnknownClient1NodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
endpoint.Addr: UnknownClient1IP,
endpoint.Port: UnknownClient1Port,
endpoint.Procspied: True,
@@ -169,7 +169,7 @@ var (
EgressByteCount: newu64(300),
}),
UnknownClient2NodeID: report.MakeNode().WithID(UnknownClient2NodeID).WithLatests(map[string]string{
UnknownClient2NodeID: report.MakeNode().WithID(UnknownClient2NodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
endpoint.Addr: UnknownClient2IP,
endpoint.Port: UnknownClient2Port,
endpoint.Procspied: True,
@@ -178,7 +178,7 @@ var (
EgressByteCount: newu64(400),
}),
UnknownClient3NodeID: report.MakeNode().WithID(UnknownClient3NodeID).WithLatests(map[string]string{
UnknownClient3NodeID: report.MakeNode().WithID(UnknownClient3NodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
endpoint.Addr: UnknownClient3IP,
endpoint.Port: UnknownClient3Port,
endpoint.Procspied: True,
@@ -187,7 +187,7 @@ var (
EgressByteCount: newu64(500),
}),
RandomClientNodeID: report.MakeNode().WithID(RandomClientNodeID).WithLatests(map[string]string{
RandomClientNodeID: report.MakeNode().WithID(RandomClientNodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
endpoint.Addr: RandomClientIP,
endpoint.Port: RandomClientPort,
endpoint.Procspied: True,
@@ -196,7 +196,7 @@ var (
EgressByteCount: newu64(600),
}),
GoogleEndpointNodeID: report.MakeNode().WithID(GoogleEndpointNodeID).WithLatests(map[string]string{
GoogleEndpointNodeID: report.MakeNode().WithID(GoogleEndpointNodeID).WithTopology(report.Endpoint).WithLatests(map[string]string{
endpoint.Addr: GoogleIP,
endpoint.Port: GooglePort,
endpoint.Procspied: True,
@@ -267,17 +267,17 @@ var (
docker.MemoryUsage: ClientContainerMemoryMetric,
}),
ServerContainerNodeID: report.MakeNodeWith(map[string]string{
docker.ContainerID: ServerContainerID,
docker.ContainerName: "task-name-5-server-aceb93e2f2b797caba01",
docker.ContainerState: docker.StateRunning,
docker.ImageID: ServerContainerImageID,
report.HostNodeID: ServerHostNodeID,
docker.LabelPrefix + render.AmazonECSContainerNameLabel: "server",
docker.LabelPrefix + "foo1": "bar1",
docker.LabelPrefix + "foo2": "bar2",
docker.LabelPrefix + "io.kubernetes.pod.name": ServerPodID,
kubernetes.PodID: ServerPodID,
kubernetes.Namespace: KubernetesNamespace,
docker.ContainerID: ServerContainerID,
docker.ContainerName: "task-name-5-server-aceb93e2f2b797caba01",
docker.ContainerState: docker.StateRunning,
docker.ImageID: ServerContainerImageID,
report.HostNodeID: ServerHostNodeID,
docker.LabelPrefix + detailed.AmazonECSContainerNameLabel: "server",
docker.LabelPrefix + "foo1": "bar1",
docker.LabelPrefix + "foo2": "bar2",
docker.LabelPrefix + "io.kubernetes.pod.name": ServerPodID,
kubernetes.PodID: ServerPodID,
kubernetes.Namespace: KubernetesNamespace,
}).WithID(ServerContainerNodeID).WithTopology(report.Container).WithParents(report.EmptySets.
Add("host", report.MakeStringSet(ServerHostNodeID)).
Add("container_image", report.MakeStringSet(ServerContainerImageNodeID)).