From 75b5e0748283b4695bba409f1fd038bdf9179c6f Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Wed, 17 Jun 2015 12:55:03 +0000 Subject: [PATCH] Add test for ContainerRenderer and make tests in /app pass. --- app/api_topologies_test.go | 6 +-- app/api_topology_test.go | 15 +++---- app/mock_reporter_test.go | 47 ++++++++++++++++++---- experimental/graphviz/handle.go | 2 +- render/mapping.go | 33 ++++++++------- render/renderable_node.go | 13 ++++++ render/topologies_test.go | 71 ++++++++++++++++++++++++++++++++- 7 files changed, 149 insertions(+), 38 deletions(-) diff --git a/app/api_topologies_test.go b/app/api_topologies_test.go index 6b5aff968..3d8494a1c 100644 --- a/app/api_topologies_test.go +++ b/app/api_topologies_test.go @@ -26,15 +26,15 @@ func TestAPITopology(t *testing.T) { } if have := topology.Stats.EdgeCount; have <= 0 { - t.Errorf("EdgeCount isn't positive: %d", have) + t.Errorf("EdgeCount isn't positive for %s: %d", topology.Name, have) } if have := topology.Stats.NodeCount; have <= 0 { - t.Errorf("NodeCount isn't positive: %d", have) + t.Errorf("NodeCount isn't positive for %s: %d", topology.Name, have) } if have := topology.Stats.NonpseudoNodeCount; have <= 0 { - t.Errorf("NonpseudoNodeCount isn't positive: %d", have) + t.Errorf("NonpseudoNodeCount isn't positive for %s: %d", topology.Name, have) } } } diff --git a/app/api_topology_test.go b/app/api_topology_test.go index 3c04c28b8..91dd772af 100644 --- a/app/api_topology_test.go +++ b/app/api_topology_test.go @@ -23,36 +23,37 @@ func TestAPITopologyApplications(t *testing.T) { t.Fatal(err) } equals(t, 4, len(topo.Nodes)) - node, ok := topo.Nodes["pid:node-a.local:23128"] + node, ok := topo.Nodes["pid:hostA:23128"] if !ok { t.Errorf("missing curl node") } equals(t, 1, len(node.Adjacency)) - equals(t, report.MakeIDList("pid:node-b.local:215"), node.Adjacency) + equals(t, report.MakeIDList("pid:hostB:215"), node.Adjacency) equals(t, report.MakeIDList( report.MakeEndpointNodeID("hostA", "192.168.1.1", "12345"), report.MakeEndpointNodeID("hostA", "192.168.1.1", "12346"), + report.MakeProcessNodeID("hostA", "23128"), report.MakeHostNodeID("hostA"), ), node.Origins) equals(t, "curl", node.LabelMajor) - equals(t, "node-a.local (23128)", node.LabelMinor) + equals(t, "hostA (23128)", node.LabelMinor) equals(t, "23128", node.Rank) equals(t, false, node.Pseudo) } { - body := getRawJSON(t, ts, "/api/topology/applications/pid:node-a.local:23128") + body := getRawJSON(t, ts, "/api/topology/applications/pid:hostA:23128") var node APINode if err := json.Unmarshal(body, &node); err != nil { t.Fatal(err) } - equals(t, "pid:node-a.local:23128", node.Node.ID) + equals(t, "pid:hostA:23128", node.Node.ID) equals(t, "curl", node.Node.LabelMajor) - equals(t, "node-a.local (23128)", node.Node.LabelMinor) + equals(t, "hostA (23128)", node.Node.LabelMinor) equals(t, false, node.Node.Pseudo) // Let's not unit-test the specific content of the detail tables } { - body := getRawJSON(t, ts, "/api/topology/applications/pid:node-a.local:23128/pid:node-b.local:215") + body := getRawJSON(t, ts, "/api/topology/applications/pid:hostA:23128/pid:hostB:215") var edge APIEdge if err := json.Unmarshal(body, &edge); err != nil { t.Fatalf("JSON parse error: %s", err) diff --git a/app/mock_reporter_test.go b/app/mock_reporter_test.go index 5529b52da..cbcba0c84 100644 --- a/app/mock_reporter_test.go +++ b/app/mock_reporter_test.go @@ -55,32 +55,63 @@ func (s StaticReport) Report() report.Report { }, NodeMetadatas: report.NodeMetadatas{ report.MakeEndpointNodeID("hostA", "192.168.1.1", "12345"): report.NodeMetadata{ + "addr": "192.168.1.1", + "port": "12345", "pid": "23128", - "name": "curl", - "domain": "node-a.local", report.HostNodeID: report.MakeHostNodeID("hostA"), }, report.MakeEndpointNodeID("hostA", "192.168.1.1", "12346"): report.NodeMetadata{ // <-- same as :12345 + "addr": "192.168.1.1", + "port": "12346", "pid": "23128", - "name": "curl", - "domain": "node-a.local", report.HostNodeID: report.MakeHostNodeID("hostA"), }, report.MakeEndpointNodeID("hostA", "192.168.1.1", "8888"): report.NodeMetadata{ + "addr": "192.168.1.1", + "port": "8888", "pid": "55100", - "name": "ssh", - "domain": "node-a.local", report.HostNodeID: report.MakeHostNodeID("hostA"), }, report.MakeEndpointNodeID("hostB", "192.168.1.2", "80"): report.NodeMetadata{ + "addr": "192.168.1.2", + "port": "80", "pid": "215", - "name": "apache", - "domain": "node-b.local", report.HostNodeID: report.MakeHostNodeID("hostB"), }, }, }, + Process: report.Topology{ + NodeMetadatas: report.NodeMetadatas{ + report.MakeProcessNodeID("hostA", "23128"): report.NodeMetadata{ + "pid": "23128", + "comm": "curl", + report.HostNodeID: report.MakeHostNodeID("hostA"), + }, + report.MakeProcessNodeID("hostA", "8888"): report.NodeMetadata{ + "pid": "8888", + "comm": "ssh", + report.HostNodeID: report.MakeHostNodeID("hostA"), + }, + report.MakeProcessNodeID("hostB", "80"): report.NodeMetadata{ + "pid": "80", + "comm": "apache", + "docker_container_id": "abcdefg", + report.HostNodeID: report.MakeHostNodeID("hostB"), + }, + }, + }, + + Container: report.Topology{ + NodeMetadatas: report.NodeMetadatas{ + report.MakeContainerNodeID("hostB", "abcdefg"): report.NodeMetadata{ + "docker_container_id": "abcdefg", + "docker_container_name": "server", + report.HostNodeID: report.MakeHostNodeID("hostB"), + }, + }, + }, + Address: report.Topology{ Adjacency: report.Adjacency{ report.MakeAdjacencyID(report.MakeAddressNodeID("hostA", "192.168.1.1")): report.MakeIDList(report.MakeAddressNodeID("hostB", "192.168.1.2"), report.MakeAddressNodeID("", "1.2.3.4")), diff --git a/experimental/graphviz/handle.go b/experimental/graphviz/handle.go index fbd560613..03c4e65a8 100644 --- a/experimental/graphviz/handle.go +++ b/experimental/graphviz/handle.go @@ -107,7 +107,7 @@ func mapFunc(r *http.Request) render.LeafMapFunc { case "hosts", "networkhost", "networkhostname": return render.NetworkHostname } - return render.ProcessPID + return render.MapProcessIdentity } func classView(r *http.Request) bool { diff --git a/render/mapping.go b/render/mapping.go index e923e93b3..edc5d5acd 100644 --- a/render/mapping.go +++ b/render/mapping.go @@ -7,9 +7,10 @@ import ( "github.com/weaveworks/scope/report" ) +// Constants are used in the tests. const ( - uncontainedID = "uncontained" - uncontainedMajor = "Uncontained" + UncontainedID = "uncontained" + UncontainedMajor = "Uncontained" humanTheInternet = "the Internet" ) @@ -44,11 +45,17 @@ type MapFunc func(RenderableNode) (RenderableNode, bool) // presences of certain keys. func MapEndpointIdentity(m report.NodeMetadata) (RenderableNode, bool) { var ( - id = fmt.Sprintf("endpoint:%s:%s:%s", getHostname(m), m["addr"], m["port"]) - major = fmt.Sprintf("%s:%s", m["addr"], m["port"]) - minor = fmt.Sprintf("%s (%s)", getHostname(m), m["pid"]) - rank = m["pid"] + id = fmt.Sprintf("endpoint:%s:%s:%s", getHostname(m), m["addr"], m["port"]) + major = fmt.Sprintf("%s:%s", m["addr"], m["port"]) + pid, ok = m["pid"] + minor = getHostname(m) + rank = major ) + + if ok { + minor = fmt.Sprintf("%s (%s)", getHostname(m), pid) + } + return NewRenderableNode(id, major, minor, rank, m), true } @@ -119,8 +126,8 @@ func MapEndpoint2Process(n RenderableNode) (RenderableNode, bool) { // must be merged with a container graph to get that info. func MapProcess2Container(n RenderableNode) (RenderableNode, bool) { id, ok := n.NodeMetadata["docker_container_id"] - if !ok { - return newPseudoNode(uncontainedID, uncontainedMajor, ""), true + if !ok || n.Pseudo { + return newDerivedPseudoNode(UncontainedID, UncontainedMajor, n), true } return newDerivedNode(id, n), true @@ -154,21 +161,13 @@ func getHostname(m report.NodeMetadata) string { return hostname } -// ProcessName takes a node NodeMetadata from a topology, and returns a -// representation with the ID based on the process name (grouping all -// processes with the same name together). -func ProcessName(m report.NodeMetadata) (RenderableNode, bool) { - show := m["pid"] != "" && m["name"] != "" - return NewRenderableNode(m["name"], m["name"], "", m["name"], m), show -} - // ProcessContainerImage maps topology nodes to the container images they run // on. If no container metadata is found, nodes are grouped into the // Uncontained node. func ProcessContainerImage(m report.NodeMetadata) (RenderableNode, bool) { var id, major, minor, rank string if m["docker_image_id"] == "" { - id, major, minor, rank = "uncontained", "Uncontained", "", "uncontained" + id, major, minor, rank = UncontainedID, UncontainedMajor, "", UncontainedID } else { id, major, minor, rank = m["docker_image_id"], m["docker_image_name"], "", m["docker_image_id"] } diff --git a/render/renderable_node.go b/render/renderable_node.go index d5ddb13dd..a83f251af 100644 --- a/render/renderable_node.go +++ b/render/renderable_node.go @@ -97,3 +97,16 @@ func newPseudoNode(id, major, minor string) RenderableNode { NodeMetadata: report.NodeMetadata{}, } } + +func newDerivedPseudoNode(id, major string, node RenderableNode) RenderableNode { + return RenderableNode{ + ID: id, + LabelMajor: major, + LabelMinor: "", + Rank: "", + Pseudo: true, + AggregateMetadata: node.AggregateMetadata, + Origins: node.Origins, + NodeMetadata: node.NodeMetadata, + } +} diff --git a/render/topologies_test.go b/render/topologies_test.go index 382e08167..0188c809f 100644 --- a/render/topologies_test.go +++ b/render/topologies_test.go @@ -54,6 +54,11 @@ var ( clientProcessNodeID = report.MakeProcessNodeID(clientHostID, clientPID) serverProcessNodeID = report.MakeProcessNodeID(serverHostID, serverPID) nonContainerProcessNodeID = report.MakeProcessNodeID(serverHostID, nonContainerPID) + + clientContainerID = "a1b2c3d4e5" + serverContainerID = "5e4d3c2b1a" + clientContainerNodeID = report.MakeContainerNodeID(clientHostID, clientContainerID) + serverContainerNodeID = report.MakeContainerNodeID(serverHostID, serverContainerID) ) var ( @@ -132,13 +137,13 @@ var ( clientProcessNodeID: report.NodeMetadata{ "pid": clientPID, "comm": "curl", - "docker_container_id": "a1b2c3d4e5", + "docker_container_id": clientContainerID, report.HostNodeID: clientHostNodeID, }, serverProcessNodeID: report.NodeMetadata{ "pid": serverPID, "comm": "apache", - "docker_container_id": "5e4d3c2b1a", + "docker_container_id": serverContainerID, report.HostNodeID: serverHostNodeID, }, nonContainerProcessNodeID: report.NodeMetadata{ @@ -149,6 +154,20 @@ var ( }, EdgeMetadatas: report.EdgeMetadatas{}, }, + Container: report.Topology{ + NodeMetadatas: report.NodeMetadatas{ + clientContainerNodeID: report.NodeMetadata{ + "docker_container_id": clientContainerID, + "docker_container_name": "client", + report.HostNodeID: clientHostNodeID, + }, + serverContainerNodeID: report.NodeMetadata{ + "docker_container_id": serverContainerID, + "docker_container_name": "server", + report.HostNodeID: serverHostNodeID, + }, + }, + }, Address: report.Topology{ Adjacency: report.Adjacency{ report.MakeAdjacencyID(clientAddressNodeID): report.MakeIDList(serverAddressNodeID), @@ -351,6 +370,54 @@ func TestProcessNameRenderer(t *testing.T) { } } +func TestContainerRenderer(t *testing.T) { + // For grouped, I've somewhat arbitrarily chosen to squash together all + // processes with the same name by removing the PID and domain (host) + // dimensions from the ID. That could be changed. + want := render.RenderableNodes{ + clientContainerID: { + ID: clientContainerID, + LabelMajor: "client", + LabelMinor: clientHostName, + Rank: "", + Pseudo: false, + Adjacency: report.MakeIDList(serverContainerID), + Origins: report.MakeIDList(clientContainerNodeID, client54001NodeID, client54002NodeID, clientProcessNodeID, clientHostNodeID), + AggregateMetadata: report.AggregateMetadata{ + report.KeyBytesIngress: 300, + report.KeyBytesEgress: 30, + }, + }, + serverContainerID: { + ID: serverContainerID, + LabelMajor: "server", + LabelMinor: serverHostName, + Rank: "", + Pseudo: false, + Adjacency: report.MakeIDList(clientContainerID, render.UncontainedID), + Origins: report.MakeIDList(serverContainerNodeID, server80NodeID, serverProcessNodeID, serverHostNodeID), + AggregateMetadata: report.AggregateMetadata{ + report.KeyBytesIngress: 150, + report.KeyBytesEgress: 1500, + }, + }, + render.UncontainedID: { + ID: render.UncontainedID, + LabelMajor: render.UncontainedMajor, + LabelMinor: "", + Rank: "", + Pseudo: true, + Origins: report.MakeIDList(nonContainerProcessNodeID, serverHostNodeID), + AggregateMetadata: report.AggregateMetadata{}, + }, + } + have := render.ContainerRenderer.Render(rpt) + have = trimNodeMetadata(have) + if !reflect.DeepEqual(want, have) { + t.Error("\n" + diff(want, have)) + } +} + func TestRenderByNetworkHostname(t *testing.T) { want := render.RenderableNodes{ "host:client.hostname.com": {