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

@@ -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))
}