From 89df11bbfd19cd33e78bdcf0cf414bcb4d1cf8df Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Thu, 13 Aug 2015 14:46:22 +0000 Subject: [PATCH 1/4] Make edge direction flow from client->server. --- probe/endpoint/reporter.go | 33 +++++++++++++++++++++++++++------ probe/endpoint/reporter_test.go | 12 ++++++------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/probe/endpoint/reporter.go b/probe/endpoint/reporter.go index 8912319f9..79c946ca0 100644 --- a/probe/endpoint/reporter.go +++ b/probe/endpoint/reporter.go @@ -77,13 +77,24 @@ func (r *Reporter) Report() (report.Report, error) { func (r *Reporter) addConnection(rpt *report.Report, c *procspy.Connection) { var ( + localIsClient = int(c.LocalPort) > int(c.RemotePort) localAddressNodeID = report.MakeAddressNodeID(r.hostID, c.LocalAddress.String()) remoteAddressNodeID = report.MakeAddressNodeID(r.hostID, c.RemoteAddress.String()) - adjecencyID = report.MakeAdjacencyID(localAddressNodeID) - edgeID = report.MakeEdgeID(localAddressNodeID, remoteAddressNodeID) + adjecencyID = "" + edgeID = "" ) - rpt.Address.Adjacency[adjecencyID] = rpt.Address.Adjacency[adjecencyID].Add(remoteAddressNodeID) + if localIsClient { + adjecencyID = report.MakeAdjacencyID(localAddressNodeID) + rpt.Address.Adjacency[adjecencyID] = rpt.Address.Adjacency[adjecencyID].Add(remoteAddressNodeID) + + edgeID = report.MakeEdgeID(localAddressNodeID, remoteAddressNodeID) + } else { + adjecencyID = report.MakeAdjacencyID(remoteAddressNodeID) + rpt.Address.Adjacency[adjecencyID] = rpt.Address.Adjacency[adjecencyID].Add(localAddressNodeID) + + edgeID = report.MakeEdgeID(remoteAddressNodeID, localAddressNodeID) + } if _, ok := rpt.Address.NodeMetadatas[localAddressNodeID]; !ok { rpt.Address.NodeMetadatas[localAddressNodeID] = report.MakeNodeMetadataWith(map[string]string{ @@ -98,11 +109,21 @@ func (r *Reporter) addConnection(rpt *report.Report, c *procspy.Connection) { var ( localEndpointNodeID = report.MakeEndpointNodeID(r.hostID, c.LocalAddress.String(), strconv.Itoa(int(c.LocalPort))) remoteEndpointNodeID = report.MakeEndpointNodeID(r.hostID, c.RemoteAddress.String(), strconv.Itoa(int(c.RemotePort))) - adjecencyID = report.MakeAdjacencyID(localEndpointNodeID) - edgeID = report.MakeEdgeID(localEndpointNodeID, remoteEndpointNodeID) + adjecencyID = "" + edgeID = "" ) - rpt.Endpoint.Adjacency[adjecencyID] = rpt.Endpoint.Adjacency[adjecencyID].Add(remoteEndpointNodeID) + if localIsClient { + adjecencyID = report.MakeAdjacencyID(localEndpointNodeID) + rpt.Endpoint.Adjacency[adjecencyID] = rpt.Endpoint.Adjacency[adjecencyID].Add(remoteEndpointNodeID) + + edgeID = report.MakeEdgeID(localEndpointNodeID, remoteEndpointNodeID) + } else { + adjecencyID = report.MakeAdjacencyID(remoteEndpointNodeID) + rpt.Endpoint.Adjacency[adjecencyID] = rpt.Endpoint.Adjacency[adjecencyID].Add(localEndpointNodeID) + + edgeID = report.MakeEdgeID(remoteEndpointNodeID, localEndpointNodeID) + } if _, ok := rpt.Endpoint.NodeMetadatas[localEndpointNodeID]; !ok { // First hit establishes NodeMetadata for scoped local address + port diff --git a/probe/endpoint/reporter_test.go b/probe/endpoint/reporter_test.go index 33a788ae5..455d18ada 100644 --- a/probe/endpoint/reporter_test.go +++ b/probe/endpoint/reporter_test.go @@ -85,14 +85,14 @@ func TestSpyNoProcesses(t *testing.T) { var ( scopedLocal = report.MakeAddressNodeID(nodeID, fixLocalAddress.String()) scopedRemote = report.MakeAddressNodeID(nodeID, fixRemoteAddress.String()) - localKey = report.MakeAdjacencyID(scopedLocal) + remoteKey = report.MakeAdjacencyID(scopedRemote) ) - if want, have := 1, len(r.Address.Adjacency[localKey]); want != have { + if want, have := 1, len(r.Address.Adjacency[remoteKey]); want != have { t.Fatalf("want %d, have %d", want, have) } - if want, have := scopedRemote, r.Address.Adjacency[localKey][0]; want != have { + if want, have := scopedLocal, r.Address.Adjacency[remoteKey][0]; want != have { t.Fatalf("want %q, have %q", want, have) } @@ -116,14 +116,14 @@ func TestSpyWithProcesses(t *testing.T) { var ( scopedLocal = report.MakeEndpointNodeID(nodeID, fixLocalAddress.String(), strconv.Itoa(int(fixLocalPort))) scopedRemote = report.MakeEndpointNodeID(nodeID, fixRemoteAddress.String(), strconv.Itoa(int(fixRemotePort))) - localKey = report.MakeAdjacencyID(scopedLocal) + remoteKey = report.MakeAdjacencyID(scopedRemote) ) - if want, have := 1, len(r.Endpoint.Adjacency[localKey]); want != have { + if want, have := 1, len(r.Endpoint.Adjacency[remoteKey]); want != have { t.Fatalf("want %d, have %d", want, have) } - if want, have := scopedRemote, r.Endpoint.Adjacency[localKey][0]; want != have { + if want, have := scopedLocal, r.Endpoint.Adjacency[remoteKey][0]; want != have { t.Fatalf("want %q, have %q", want, have) } From 60d1b503c1fd38d15865a5c9d86277f5bbea295b Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Mon, 17 Aug 2015 11:02:56 +0000 Subject: [PATCH 2/4] Deal with directed edges in the rest of the codebase. --- app/api_topology_test.go | 2 +- render/detailed_node_test.go | 94 +++++++++++++------------- render/expected/expected.go | 127 ++++++++++++++++++----------------- render/mapping.go | 44 +++++++----- render/render.go | 35 +++++++--- report/topology.go | 18 +++-- test/report_fixture.go | 40 +++++------ 7 files changed, 195 insertions(+), 165 deletions(-) diff --git a/app/api_topology_test.go b/app/api_topology_test.go index 0cc249faf..9d36b0dd2 100644 --- a/app/api_topology_test.go +++ b/app/api_topology_test.go @@ -132,7 +132,7 @@ func TestAPITopologyHosts(t *testing.T) { // Let's not unit-test the specific content of the detail tables } { - body := getRawJSON(t, ts, fmt.Sprintf("/api/topology/hosts/%s/%s", expected.ServerHostRenderedID, expected.ClientHostRenderedID)) + body := getRawJSON(t, ts, fmt.Sprintf("/api/topology/hosts/%s/%s", expected.ClientHostRenderedID, expected.ServerHostRenderedID)) var edge APIEdge if err := json.Unmarshal(body, &edge); err != nil { t.Fatalf("JSON parse error: %s", err) diff --git a/render/detailed_node_test.go b/render/detailed_node_test.go index 9219fee48..b522da380 100644 --- a/render/detailed_node_test.go +++ b/render/detailed_node_test.go @@ -1,7 +1,7 @@ package render_test import ( - "fmt" + // "fmt" "reflect" "testing" @@ -121,15 +121,15 @@ func TestMakeDetailedContainerNode(t *testing.T) { LabelMinor: test.ServerHostName, Pseudo: false, Tables: []render.Table{ - { - Title: "Connections", - Numeric: true, - Rank: 100, - Rows: []render.Row{ - {"Egress packet rate", "75", "packets/sec"}, - {"Egress byte rate", "750", "Bps"}, - }, - }, + // { + // Title: "Connections", + // Numeric: true, + // Rank: 100, + // Rows: []render.Row{ + // {"Egress packet rate", "75", "packets/sec"}, + // {"Egress byte rate", "750", "Bps"}, + // }, + // }, { Title: "Origin Container", Numeric: false, @@ -159,43 +159,43 @@ func TestMakeDetailedContainerNode(t *testing.T) { {"Operating system", "Linux", ""}, }, }, - { - Title: "Connection Details", - Numeric: false, - Rows: []render.Row{ - {"Local", "Remote", ""}, - { - fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010), - "", - }, - { - fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020), - "", - }, - { - fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020), - "", - }, - { - fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54001), - "", - }, - { - fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54002), - "", - }, - { - fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345), - "", - }, - }, - }, + // { + // Title: "Connection Details", + // Numeric: false, + // Rows: []render.Row{ + // {"Local", "Remote", ""}, + // { + // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + // fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010), + // "", + // }, + // { + // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + // fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020), + // "", + // }, + // { + // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + // fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020), + // "", + // }, + // { + // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + // fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54001), + // "", + // }, + // { + // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + // fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54002), + // "", + // }, + // { + // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + // fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345), + // "", + // }, + // }, + // }, }, } if !reflect.DeepEqual(want, have) { diff --git a/render/expected/expected.go b/render/expected/expected.go index 8928bbdc0..472feea87 100644 --- a/render/expected/expected.go +++ b/render/expected/expected.go @@ -13,28 +13,45 @@ var ( uncontainedServerID = render.MakePseudoNodeID(render.UncontainedID, test.ServerHostName) unknownPseudoNode1ID = render.MakePseudoNodeID("10.10.10.10", test.ServerIP, "80") unknownPseudoNode2ID = render.MakePseudoNodeID("10.10.10.11", test.ServerIP, "80") - unknownPseudoNode1 = render.RenderableNode{ - ID: unknownPseudoNode1ID, - LabelMajor: "10.10.10.10", - Pseudo: true, - NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{}, + unknownPseudoNode1 = func(adjacency report.IDList) render.RenderableNode { + return render.RenderableNode{ + ID: unknownPseudoNode1ID, + LabelMajor: "10.10.10.10", + Pseudo: true, + NodeMetadata: report.MakeNodeMetadata(), + EdgeMetadata: report.EdgeMetadata{ + EgressPacketCount: newu64(40), + EgressByteCount: newu64(400), + }, + Adjacency: adjacency, + } } - unknownPseudoNode2 = render.RenderableNode{ - ID: unknownPseudoNode2ID, - LabelMajor: "10.10.10.11", - Pseudo: true, - NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{}, + unknownPseudoNode2 = func(adjacency report.IDList) render.RenderableNode { + return render.RenderableNode{ + ID: unknownPseudoNode2ID, + LabelMajor: "10.10.10.11", + Pseudo: true, + NodeMetadata: report.MakeNodeMetadata(), + EdgeMetadata: report.EdgeMetadata{ + EgressPacketCount: newu64(50), + EgressByteCount: newu64(500), + }, + Adjacency: adjacency, + } } - theInternetNode = render.RenderableNode{ - ID: render.TheInternetID, - LabelMajor: render.TheInternetMajor, - Pseudo: true, - NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{}, + theInternetNode = func(adjacency report.IDList) render.RenderableNode { + return render.RenderableNode{ + ID: render.TheInternetID, + LabelMajor: render.TheInternetMajor, + Pseudo: true, + NodeMetadata: report.MakeNodeMetadata(), + EdgeMetadata: report.EdgeMetadata{ + EgressPacketCount: newu64(60), + EgressByteCount: newu64(600), + }, + Adjacency: adjacency, + } } - ClientProcess1ID = render.MakeProcessID(test.ClientHostID, test.Client1PID) ClientProcess2ID = render.MakeProcessID(test.ClientHostID, test.Client2PID) ServerProcessID = render.MakeProcessID(test.ServerHostID, test.ServerPID) @@ -83,23 +100,14 @@ var ( LabelMinor: fmt.Sprintf("%s (%s)", test.ServerHostID, test.ServerPID), Rank: test.ServerComm, Pseudo: false, - Adjacency: report.MakeIDList( - ClientProcess1ID, - ClientProcess2ID, - unknownPseudoNode1ID, - unknownPseudoNode2ID, - render.TheInternetID, - ), + Adjacency: report.MakeIDList(), Origins: report.MakeIDList( test.Server80NodeID, test.ServerProcessNodeID, test.ServerHostNodeID, ), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{ - EgressPacketCount: newu64(150), - EgressByteCount: newu64(1500), - }, + EdgeMetadata: report.EdgeMetadata{}, }, nonContainerProcessID: { ID: nonContainerProcessID, @@ -115,9 +123,9 @@ var ( NodeMetadata: report.MakeNodeMetadata(), EdgeMetadata: report.EdgeMetadata{}, }, - unknownPseudoNode1ID: unknownPseudoNode1, - unknownPseudoNode2ID: unknownPseudoNode2, - render.TheInternetID: theInternetNode, + unknownPseudoNode1ID: unknownPseudoNode1(report.MakeIDList(ServerProcessID)), + unknownPseudoNode2ID: unknownPseudoNode2(report.MakeIDList(ServerProcessID)), + render.TheInternetID: theInternetNode(report.MakeIDList(ServerProcessID)), } RenderedProcessNames = render.RenderableNodes{ @@ -147,22 +155,14 @@ var ( LabelMinor: "", Rank: "apache", Pseudo: false, - Adjacency: report.MakeIDList( - "curl", - unknownPseudoNode1ID, - unknownPseudoNode2ID, - render.TheInternetID, - ), + Adjacency: report.MakeIDList(), Origins: report.MakeIDList( test.Server80NodeID, test.ServerProcessNodeID, test.ServerHostNodeID, ), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{ - EgressPacketCount: newu64(150), - EgressByteCount: newu64(1500), - }, + EdgeMetadata: report.EdgeMetadata{}, }, "bash": { ID: "bash", @@ -177,9 +177,9 @@ var ( NodeMetadata: report.MakeNodeMetadata(), EdgeMetadata: report.EdgeMetadata{}, }, - unknownPseudoNode1ID: unknownPseudoNode1, - unknownPseudoNode2ID: unknownPseudoNode2, - render.TheInternetID: theInternetNode, + unknownPseudoNode1ID: unknownPseudoNode1(report.MakeIDList("apache")), + unknownPseudoNode2ID: unknownPseudoNode2(report.MakeIDList("apache")), + render.TheInternetID: theInternetNode(report.MakeIDList("apache")), } RenderedContainers = render.RenderableNodes{ @@ -210,7 +210,7 @@ var ( LabelMinor: test.ServerHostName, Rank: test.ServerContainerImageID, Pseudo: false, - Adjacency: report.MakeIDList(test.ClientContainerID, render.TheInternetID), + Adjacency: report.MakeIDList(), Origins: report.MakeIDList( test.ServerContainerNodeID, test.Server80NodeID, @@ -218,10 +218,7 @@ var ( test.ServerHostNodeID, ), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{ - EgressPacketCount: newu64(150), - EgressByteCount: newu64(1500), - }, + EdgeMetadata: report.EdgeMetadata{}, }, uncontainedServerID: { ID: uncontainedServerID, @@ -236,7 +233,7 @@ var ( NodeMetadata: report.MakeNodeMetadata(), EdgeMetadata: report.EdgeMetadata{}, }, - render.TheInternetID: theInternetNode, + render.TheInternetID: theInternetNode(report.MakeIDList(test.ServerContainerID)), } RenderedContainerImages = render.RenderableNodes{ @@ -268,7 +265,7 @@ var ( LabelMinor: "", Rank: test.ServerContainerImageName, Pseudo: false, - Adjacency: report.MakeIDList(test.ClientContainerImageName, render.TheInternetID), + Adjacency: report.MakeIDList(), Origins: report.MakeIDList( test.ServerContainerImageNodeID, test.ServerContainerNodeID, @@ -276,10 +273,7 @@ var ( test.ServerProcessNodeID, test.ServerHostNodeID), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{ - EgressPacketCount: newu64(150), - EgressByteCount: newu64(1500), - }, + EdgeMetadata: report.EdgeMetadata{}, }, uncontainedServerID: { ID: uncontainedServerID, @@ -294,7 +288,7 @@ var ( NodeMetadata: report.MakeNodeMetadata(), EdgeMetadata: report.EdgeMetadata{}, }, - render.TheInternetID: theInternetNode, + render.TheInternetID: theInternetNode(report.MakeIDList(test.ServerContainerImageName)), } ServerHostRenderedID = render.MakeHostID(test.ServerHostID) @@ -309,15 +303,13 @@ var ( LabelMinor: "hostname.com", // after first . Rank: "hostname.com", Pseudo: false, - Adjacency: report.MakeIDList(ClientHostRenderedID, render.TheInternetID, pseudoHostID1, pseudoHostID2), + Adjacency: report.MakeIDList(), Origins: report.MakeIDList( test.ServerHostNodeID, test.ServerAddressNodeID, ), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{ - MaxConnCountTCP: newu64(3), - }, + EdgeMetadata: report.EdgeMetadata{}, }, ClientHostRenderedID: { ID: ClientHostRenderedID, @@ -339,6 +331,7 @@ var ( ID: pseudoHostID1, LabelMajor: "10.10.10.10", Pseudo: true, + Adjacency: report.MakeIDList(ServerHostRenderedID), NodeMetadata: report.MakeNodeMetadata(), EdgeMetadata: report.EdgeMetadata{}, }, @@ -346,10 +339,18 @@ var ( ID: pseudoHostID2, LabelMajor: "10.10.10.11", Pseudo: true, + Adjacency: report.MakeIDList(ServerHostRenderedID), + NodeMetadata: report.MakeNodeMetadata(), + EdgeMetadata: report.EdgeMetadata{}, + }, + render.TheInternetID: { + ID: render.TheInternetID, + LabelMajor: render.TheInternetMajor, + Pseudo: true, + Adjacency: report.MakeIDList(ServerHostRenderedID), NodeMetadata: report.MakeNodeMetadata(), EdgeMetadata: report.EdgeMetadata{}, }, - render.TheInternetID: theInternetNode, } ) diff --git a/render/mapping.go b/render/mapping.go index 66da21d6d..5e77db13c 100644 --- a/render/mapping.go +++ b/render/mapping.go @@ -33,11 +33,12 @@ const ( // rendered topology. type LeafMapFunc func(report.NodeMetadata) (RenderableNode, bool) -// PseudoFunc creates RenderableNode representing pseudo nodes given the dstNodeID. -// The srcNode renderable node is essentially from MapFunc, representing one of -// the rendered nodes this pseudo node refers to. srcNodeID and dstNodeID are -// node IDs prior to mapping. -type PseudoFunc func(srcNodeID string, srcNode RenderableNode, dstNodeID string, local report.Networks) (RenderableNode, bool) +// PseudoFunc creates RenderableNode representing pseudo nodes given the nodeID. +// dstNodeID is the node id of one of the nodes this node is attached to. +// nodeID and dstNodeID are node IDs prior to mapping. isClient indicated the direction +// of the edge to dstNodeID - true indicates nodeID is the client, false indicates +// nodeID is the server. +type PseudoFunc func(nodeID string, dstNodeID string, isClient bool, local report.Networks) (RenderableNode, bool) // MapFunc is anything which can take an arbitrary RenderableNode and // return another RenderableNode. @@ -323,29 +324,36 @@ func MapAddress2Host(n RenderableNode) (RenderableNode, bool) { // the report's local networks. Otherwise, the returned function will // produce a single pseudo node per (dst address, src address, src port). func GenericPseudoNode(addresser func(id string) net.IP) PseudoFunc { - return func(src string, srcMapped RenderableNode, dst string, local report.Networks) (RenderableNode, bool) { - // Use the addresser to extract the destination IP - dstNodeAddr := addresser(dst) - + return func(nodeID, dstNodeId string, isClient bool, local report.Networks) (RenderableNode, bool) { + // Use the addresser to extract the IP of the missing node + nodeAddr := addresser(nodeID) // If the dstNodeAddr is not in a network local to this report, we emit an // internet node - if !local.Contains(dstNodeAddr) { + if !local.Contains(nodeAddr) { return newPseudoNode(TheInternetID, TheInternetMajor, ""), true } - // Otherwise, the rule for non-internet psuedo nodes; emit 1 new node for each - // dstNodeAddr, srcNodeAddr, srcNodePort. - srcNodeAddr, srcNodePort := trySplitAddr(src) + if isClient { + // If the client node is missing, generate a single pseudo node for every (client ip, server ip, server por) + serverIP, serverPort := trySplitAddr(dstNodeId) + outputID := MakePseudoNodeID(nodeAddr.String(), serverIP, serverPort) + major := nodeAddr.String() + return newPseudoNode(outputID, major, ""), true + } - outputID := MakePseudoNodeID(dstNodeAddr.String(), srcNodeAddr, srcNodePort) - major := dstNodeAddr.String() - return newPseudoNode(outputID, major, ""), true + // Othereise (the server node is missing), generate a pseudo node for every (server ip, server port) + serverIP, serverPort := trySplitAddr(nodeID) + outputID := MakePseudoNodeID(serverIP, serverPort) + if serverPort != "" { + return newPseudoNode(outputID, serverIP+":"+serverPort, ""), true + } + return newPseudoNode(outputID, serverIP, ""), true } } // PanicPseudoNode just panics; it is for Topologies without edges -func PanicPseudoNode(src string, srcMapped RenderableNode, dst string, local report.Networks) (RenderableNode, bool) { - panic(dst) +func PanicPseudoNode(src, dst string, isClient bool, local report.Networks) (RenderableNode, bool) { + panic(src) } // trySplitAddr is basically ParseArbitraryNodeID, since its callsites diff --git a/render/render.go b/render/render.go index e80de1781..f58d62f95 100644 --- a/render/render.go +++ b/render/render.go @@ -179,20 +179,41 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes { // Walk the graph and make connections. for src, dsts := range t.Adjacency { - var ( - srcNodeID, ok = report.ParseAdjacencyID(src) - srcRenderableID = source2mapped[srcNodeID] // must exist - srcRenderableNode = nodes[srcRenderableID] // must exist - ) + srcNodeID, ok := report.ParseAdjacencyID(src) if !ok { log.Printf("bad adjacency ID %q", src) continue } + var ( + srcRenderableID, ok1 = source2mapped[srcNodeID] + srcRenderableNode = nodes[srcRenderableID] + ) + if !ok1 { + // One of the entries in dsts must be a non-pseudo node + var existingDstNodeID string + for _, dstNodeID := range dsts { + if _, ok := source2mapped[dstNodeID]; ok { + existingDstNodeID = dstNodeID + break + } + } + + pseudoNode, ok := m.Pseudo(srcNodeID, existingDstNodeID, true, localNetworks) + if !ok { + continue + } + + srcRenderableID = pseudoNode.ID + srcRenderableNode = pseudoNode + nodes[srcRenderableID] = srcRenderableNode + source2mapped[srcNodeID] = srcRenderableID + } + for _, dstNodeID := range dsts { dstRenderableID, ok := source2mapped[dstNodeID] if !ok { - pseudoNode, ok := m.Pseudo(srcNodeID, srcRenderableNode, dstNodeID, localNetworks) + pseudoNode, ok := m.Pseudo(dstNodeID, srcNodeID, false, localNetworks) if !ok { continue } @@ -202,12 +223,10 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes { } srcRenderableNode.Adjacency = srcRenderableNode.Adjacency.Add(dstRenderableID) - srcRenderableNode.Origins = srcRenderableNode.Origins.Add(srcNodeID) edgeID := report.MakeEdgeID(srcNodeID, dstNodeID) if md, ok := t.EdgeMetadatas[edgeID]; ok { srcRenderableNode.EdgeMetadata.Merge(md) } - } nodes[srcRenderableID] = srcRenderableNode diff --git a/report/topology.go b/report/topology.go index 2ca9b3e0a..346805f60 100644 --- a/report/topology.go +++ b/report/topology.go @@ -85,8 +85,11 @@ func (t Topology) Validate() error { errs = append(errs, fmt.Sprintf("invalid edge ID %q", edgeID)) continue } + // For each edge, at least one of the ends must exist in nodemetadata if _, ok := t.NodeMetadatas[srcNodeID]; !ok { - errs = append(errs, fmt.Sprintf("node metadata missing for source node ID %q (from edge %q)", srcNodeID, edgeID)) + if _, ok := t.NodeMetadatas[dstNodeID]; !ok { + errs = append(errs, fmt.Sprintf("node metadatas missing for edge %q", edgeID)) + } } dstNodeIDs, ok := t.Adjacency[MakeAdjacencyID(srcNodeID)] if !ok { @@ -99,14 +102,19 @@ func (t Topology) Validate() error { } // Check all adjancency keys has entries in NodeMetadata. - for adjacencyID := range t.Adjacency { - nodeID, ok := ParseAdjacencyID(adjacencyID) + for adjacencyID, dsts := range t.Adjacency { + srcNodeID, ok := ParseAdjacencyID(adjacencyID) if !ok { errs = append(errs, fmt.Sprintf("invalid adjacency ID %q", adjacencyID)) continue } - if _, ok := t.NodeMetadatas[nodeID]; !ok { - errs = append(errs, fmt.Sprintf("node metadata missing for source node %q (from adjacency %q)", nodeID, adjacencyID)) + for _, dstNodeID := range dsts { + // For each edge, at least one of the ends must exist in nodemetadata + if _, ok := t.NodeMetadatas[srcNodeID]; !ok { + if _, ok := t.NodeMetadatas[dstNodeID]; !ok { + errs = append(errs, fmt.Sprintf("node metadata missing from adjacency %q -> %q", srcNodeID, dstNodeID)) + } + } } } diff --git a/test/report_fixture.go b/test/report_fixture.go index fe3d1db67..b84314239 100644 --- a/test/report_fixture.go +++ b/test/report_fixture.go @@ -79,11 +79,12 @@ var ( Report = report.Report{ Endpoint: report.Topology{ Adjacency: report.Adjacency{ - report.MakeAdjacencyID(Client54001NodeID): report.MakeIDList(Server80NodeID), - report.MakeAdjacencyID(Client54002NodeID): report.MakeIDList(Server80NodeID), - report.MakeAdjacencyID(Server80NodeID): report.MakeIDList( - Client54001NodeID, Client54002NodeID, UnknownClient1NodeID, UnknownClient2NodeID, - UnknownClient3NodeID, RandomClientNodeID), + report.MakeAdjacencyID(Client54001NodeID): report.MakeIDList(Server80NodeID), + report.MakeAdjacencyID(Client54002NodeID): report.MakeIDList(Server80NodeID), + report.MakeAdjacencyID(UnknownClient1NodeID): report.MakeIDList(Server80NodeID), + report.MakeAdjacencyID(UnknownClient2NodeID): report.MakeIDList(Server80NodeID), + report.MakeAdjacencyID(UnknownClient3NodeID): report.MakeIDList(Server80NodeID), + report.MakeAdjacencyID(RandomClientNodeID): report.MakeIDList(Server80NodeID), }, NodeMetadatas: report.NodeMetadatas{ // NodeMetadata is arbitrary. We're free to put only precisely what we @@ -117,27 +118,22 @@ var ( EgressPacketCount: newu64(20), EgressByteCount: newu64(200), }, - - report.MakeEdgeID(Server80NodeID, Client54001NodeID): report.EdgeMetadata{ - EgressPacketCount: newu64(10), - EgressByteCount: newu64(100), - }, - report.MakeEdgeID(Server80NodeID, Client54002NodeID): report.EdgeMetadata{ - EgressPacketCount: newu64(20), - EgressByteCount: newu64(200), - }, - report.MakeEdgeID(Server80NodeID, UnknownClient1NodeID): report.EdgeMetadata{ + report.MakeEdgeID(UnknownClient1NodeID, Server80NodeID): report.EdgeMetadata{ EgressPacketCount: newu64(30), EgressByteCount: newu64(300), }, - report.MakeEdgeID(Server80NodeID, UnknownClient2NodeID): report.EdgeMetadata{ + report.MakeEdgeID(UnknownClient2NodeID, Server80NodeID): report.EdgeMetadata{ EgressPacketCount: newu64(40), EgressByteCount: newu64(400), }, - report.MakeEdgeID(Server80NodeID, UnknownClient3NodeID): report.EdgeMetadata{ + report.MakeEdgeID(UnknownClient3NodeID, Server80NodeID): report.EdgeMetadata{ EgressPacketCount: newu64(50), EgressByteCount: newu64(500), }, + report.MakeEdgeID(RandomClientNodeID, Server80NodeID): report.EdgeMetadata{ + EgressPacketCount: newu64(60), + EgressByteCount: newu64(600), + }, }, }, Process: report.Topology{ @@ -201,9 +197,10 @@ var ( }, Address: report.Topology{ Adjacency: report.Adjacency{ - report.MakeAdjacencyID(ClientAddressNodeID): report.MakeIDList(ServerAddressNodeID), - report.MakeAdjacencyID(ServerAddressNodeID): report.MakeIDList( - ClientAddressNodeID, UnknownAddress1NodeID, UnknownAddress2NodeID, RandomAddressNodeID), // no backlinks to unknown/random + report.MakeAdjacencyID(ClientAddressNodeID): report.MakeIDList(ServerAddressNodeID), + report.MakeAdjacencyID(UnknownAddress1NodeID): report.MakeIDList(ServerAddressNodeID), + report.MakeAdjacencyID(UnknownAddress2NodeID): report.MakeIDList(ServerAddressNodeID), + report.MakeAdjacencyID(RandomAddressNodeID): report.MakeIDList(ServerAddressNodeID), }, NodeMetadatas: report.NodeMetadatas{ ClientAddressNodeID: report.MakeNodeMetadataWith(map[string]string{ @@ -219,9 +216,6 @@ var ( report.MakeEdgeID(ClientAddressNodeID, ServerAddressNodeID): report.EdgeMetadata{ MaxConnCountTCP: newu64(3), }, - report.MakeEdgeID(ServerAddressNodeID, ClientAddressNodeID): report.EdgeMetadata{ - MaxConnCountTCP: newu64(3), - }, }, }, Host: report.Topology{ From 7fb1a98eca31e3d34ace98de5642a277313ed034 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Mon, 17 Aug 2015 12:46:11 +0000 Subject: [PATCH 3/4] Update detail node logic to deal with connection directionality; also correctly merge edge metadata for pseudo nodes. --- render/detailed_node.go | 38 +++++++++++++-- render/detailed_node_test.go | 94 ++++++++++++++++++------------------ render/expected/expected.go | 28 ++++++++--- render/render.go | 47 +++++++++++------- 4 files changed, 131 insertions(+), 76 deletions(-) diff --git a/render/detailed_node.go b/render/detailed_node.go index e03506f9d..11585a692 100644 --- a/render/detailed_node.go +++ b/render/detailed_node.go @@ -48,6 +48,23 @@ type Row struct { ValueMinor string `json:"value_minor,omitempty"` // e.g. KB/s } +type rows []Row + +func (r rows) Len() int { return len(r) } +func (r rows) Swap(i, j int) { r[i], r[j] = r[j], r[i] } +func (r rows) Less(i, j int) bool { + switch { + case r[i].Key != r[j].Key: + return r[i].Key < r[j].Key + + case r[i].ValueMajor != r[j].ValueMajor: + return r[i].ValueMajor < r[j].ValueMajor + + default: + return r[i].ValueMinor < r[j].ValueMinor + } +} + type tables []Table func (t tables) Len() int { return len(t) } @@ -118,6 +135,7 @@ func MakeDetailedNode(r report.Report, n RenderableNode) DetailedNode { } } if len(connections) > 0 { + sort.Sort(rows(connections)) tables = append(tables, connectionDetailsTable(connections)) } @@ -166,16 +184,26 @@ func connectionDetailsRows(topology report.Topology, originID string) []Row { if !ok { return rows } - adjacencies := topology.Adjacency[report.MakeAdjacencyID(originID)] - sort.Strings(adjacencies) - for _, nodeID := range adjacencies { - if remote, ok := labeler(nodeID); ok { + for _, serverNodeID := range topology.Adjacency[report.MakeAdjacencyID(originID)] { + if remote, ok := labeler(serverNodeID); ok { rows = append(rows, Row{ Key: local, ValueMajor: remote, }) } } + for clientAdjID, serverNodeIDs := range topology.Adjacency { + if serverNodeIDs.Contains(originID) { + if clientNodeID, ok := report.ParseAdjacencyID(clientAdjID); ok { + if remote, ok := labeler(clientNodeID); ok { + rows = append(rows, Row{ + Key: remote, + ValueMajor: local, + }) + } + } + } + } return rows } @@ -183,7 +211,7 @@ func connectionDetailsTable(connectionRows []Row) Table { return Table{ Title: "Connection Details", Numeric: false, - Rows: append([]Row{{Key: "Local", ValueMajor: "Remote"}}, connectionRows...), + Rows: append([]Row{{Key: "Client", ValueMajor: "Server"}}, connectionRows...), Rank: endpointRank, } } diff --git a/render/detailed_node_test.go b/render/detailed_node_test.go index b522da380..242f24d56 100644 --- a/render/detailed_node_test.go +++ b/render/detailed_node_test.go @@ -1,7 +1,7 @@ package render_test import ( - // "fmt" + "fmt" "reflect" "testing" @@ -121,15 +121,15 @@ func TestMakeDetailedContainerNode(t *testing.T) { LabelMinor: test.ServerHostName, Pseudo: false, Tables: []render.Table{ - // { - // Title: "Connections", - // Numeric: true, - // Rank: 100, - // Rows: []render.Row{ - // {"Egress packet rate", "75", "packets/sec"}, - // {"Egress byte rate", "750", "Bps"}, - // }, - // }, + { + Title: "Connections", + Numeric: true, + Rank: 100, + Rows: []render.Row{ + {"Egress packet rate", "105", "packets/sec"}, + {"Egress byte rate", "1.0", "KBps"}, + }, + }, { Title: "Origin Container", Numeric: false, @@ -159,43 +159,43 @@ func TestMakeDetailedContainerNode(t *testing.T) { {"Operating system", "Linux", ""}, }, }, - // { - // Title: "Connection Details", - // Numeric: false, - // Rows: []render.Row{ - // {"Local", "Remote", ""}, - // { - // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - // fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010), - // "", - // }, - // { - // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - // fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020), - // "", - // }, - // { - // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - // fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020), - // "", - // }, - // { - // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - // fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54001), - // "", - // }, - // { - // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - // fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54002), - // "", - // }, - // { - // fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), - // fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345), - // "", - // }, - // }, - // }, + { + Title: "Connection Details", + Numeric: false, + Rows: []render.Row{ + {"Client", "Server", ""}, + { + fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010), + fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + "", + }, + { + fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020), + fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + "", + }, + { + fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020), + fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + "", + }, + { + fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54001), + fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + "", + }, + { + fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54002), + fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + "", + }, + { + fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345), + fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), + "", + }, + }, + }, }, } if !reflect.DeepEqual(want, have) { diff --git a/render/expected/expected.go b/render/expected/expected.go index 472feea87..194d99f2b 100644 --- a/render/expected/expected.go +++ b/render/expected/expected.go @@ -20,8 +20,8 @@ var ( Pseudo: true, NodeMetadata: report.MakeNodeMetadata(), EdgeMetadata: report.EdgeMetadata{ - EgressPacketCount: newu64(40), - EgressByteCount: newu64(400), + EgressPacketCount: newu64(70), + EgressByteCount: newu64(700), }, Adjacency: adjacency, } @@ -107,7 +107,10 @@ var ( test.ServerHostNodeID, ), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{}, + EdgeMetadata: report.EdgeMetadata{ + EgressPacketCount: newu64(210), + EgressByteCount: newu64(2100), + }, }, nonContainerProcessID: { ID: nonContainerProcessID, @@ -162,7 +165,10 @@ var ( test.ServerHostNodeID, ), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{}, + EdgeMetadata: report.EdgeMetadata{ + EgressPacketCount: newu64(210), + EgressByteCount: newu64(2100), + }, }, "bash": { ID: "bash", @@ -218,7 +224,10 @@ var ( test.ServerHostNodeID, ), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{}, + EdgeMetadata: report.EdgeMetadata{ + EgressPacketCount: newu64(210), + EgressByteCount: newu64(2100), + }, }, uncontainedServerID: { ID: uncontainedServerID, @@ -273,7 +282,10 @@ var ( test.ServerProcessNodeID, test.ServerHostNodeID), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{}, + EdgeMetadata: report.EdgeMetadata{ + EgressPacketCount: newu64(210), + EgressByteCount: newu64(2100), + }, }, uncontainedServerID: { ID: uncontainedServerID, @@ -309,7 +321,9 @@ var ( test.ServerAddressNodeID, ), NodeMetadata: report.MakeNodeMetadata(), - EdgeMetadata: report.EdgeMetadata{}, + EdgeMetadata: report.EdgeMetadata{ + MaxConnCountTCP: newu64(3), + }, }, ClientHostRenderedID: { ID: ClientHostRenderedID, diff --git a/render/render.go b/render/render.go index f58d62f95..d9dfcafb4 100644 --- a/render/render.go +++ b/render/render.go @@ -177,6 +177,24 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes { source2mapped[nodeID] = mapped.ID } + mkPseudoNode := func(srcID, dstId string, srcIsClient bool) (string, bool) { + pseudoNode, ok := m.Pseudo(srcID, dstId, srcIsClient, localNetworks) + if !ok { + return "", false + } + // TODO(tomwilkie): we should propagate origin nodes for pseudo nodes. + // Not worth doing until they are selectable in the UI + // pseudoNode.Origins = pseudoNode.Origins.Add(srcID) + existing, ok := nodes[pseudoNode.ID] + if ok { + pseudoNode.Merge(existing) + } + + nodes[pseudoNode.ID] = pseudoNode + source2mapped[pseudoNode.ID] = srcID + return pseudoNode.ID, true + } + // Walk the graph and make connections. for src, dsts := range t.Adjacency { srcNodeID, ok := report.ParseAdjacencyID(src) @@ -185,11 +203,8 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes { continue } - var ( - srcRenderableID, ok1 = source2mapped[srcNodeID] - srcRenderableNode = nodes[srcRenderableID] - ) - if !ok1 { + srcRenderableID, ok := source2mapped[srcNodeID] + if !ok { // One of the entries in dsts must be a non-pseudo node var existingDstNodeID string for _, dstNodeID := range dsts { @@ -199,33 +214,31 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes { } } - pseudoNode, ok := m.Pseudo(srcNodeID, existingDstNodeID, true, localNetworks) + srcRenderableID, ok = mkPseudoNode(srcNodeID, existingDstNodeID, true) if !ok { continue } - - srcRenderableID = pseudoNode.ID - srcRenderableNode = pseudoNode - nodes[srcRenderableID] = srcRenderableNode - source2mapped[srcNodeID] = srcRenderableID } + srcRenderableNode := nodes[srcRenderableID] for _, dstNodeID := range dsts { dstRenderableID, ok := source2mapped[dstNodeID] if !ok { - pseudoNode, ok := m.Pseudo(dstNodeID, srcNodeID, false, localNetworks) + dstRenderableID, ok = mkPseudoNode(dstNodeID, srcNodeID, false) if !ok { continue } - dstRenderableID = pseudoNode.ID - nodes[dstRenderableID] = pseudoNode - source2mapped[dstNodeID] = dstRenderableID } + dstRenderableNode := nodes[dstRenderableID] srcRenderableNode.Adjacency = srcRenderableNode.Adjacency.Add(dstRenderableID) - edgeID := report.MakeEdgeID(srcNodeID, dstNodeID) - if md, ok := t.EdgeMetadatas[edgeID]; ok { + + // We propagate edge metadata to nodes on both ends of the edges. + // TODO we should 'reverse' one end of the edge meta data - ingress -> egress etc. + if md, ok := t.EdgeMetadatas[report.MakeEdgeID(srcNodeID, dstNodeID)]; ok { srcRenderableNode.EdgeMetadata.Merge(md) + dstRenderableNode.EdgeMetadata.Merge(md) + nodes[dstRenderableID] = dstRenderableNode } } From 6f1f45309870370a81aba20d2e6cbe02de10f700 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Mon, 17 Aug 2015 15:51:00 +0000 Subject: [PATCH 4/4] Review feedback --- probe/endpoint/reporter.go | 20 ++++++++--------- render/detailed_node.go | 43 +++++++++++++++++++++++------------- render/detailed_node_test.go | 4 ++-- render/mapping.go | 32 +++++++++++++-------------- render/render.go | 6 ++--- 5 files changed, 59 insertions(+), 46 deletions(-) diff --git a/probe/endpoint/reporter.go b/probe/endpoint/reporter.go index 79c946ca0..0c7c5bfad 100644 --- a/probe/endpoint/reporter.go +++ b/probe/endpoint/reporter.go @@ -80,18 +80,18 @@ func (r *Reporter) addConnection(rpt *report.Report, c *procspy.Connection) { localIsClient = int(c.LocalPort) > int(c.RemotePort) localAddressNodeID = report.MakeAddressNodeID(r.hostID, c.LocalAddress.String()) remoteAddressNodeID = report.MakeAddressNodeID(r.hostID, c.RemoteAddress.String()) - adjecencyID = "" + adjacencyID = "" edgeID = "" ) if localIsClient { - adjecencyID = report.MakeAdjacencyID(localAddressNodeID) - rpt.Address.Adjacency[adjecencyID] = rpt.Address.Adjacency[adjecencyID].Add(remoteAddressNodeID) + adjacencyID = report.MakeAdjacencyID(localAddressNodeID) + rpt.Address.Adjacency[adjacencyID] = rpt.Address.Adjacency[adjacencyID].Add(remoteAddressNodeID) edgeID = report.MakeEdgeID(localAddressNodeID, remoteAddressNodeID) } else { - adjecencyID = report.MakeAdjacencyID(remoteAddressNodeID) - rpt.Address.Adjacency[adjecencyID] = rpt.Address.Adjacency[adjecencyID].Add(localAddressNodeID) + adjacencyID = report.MakeAdjacencyID(remoteAddressNodeID) + rpt.Address.Adjacency[adjacencyID] = rpt.Address.Adjacency[adjacencyID].Add(localAddressNodeID) edgeID = report.MakeEdgeID(remoteAddressNodeID, localAddressNodeID) } @@ -109,18 +109,18 @@ func (r *Reporter) addConnection(rpt *report.Report, c *procspy.Connection) { var ( localEndpointNodeID = report.MakeEndpointNodeID(r.hostID, c.LocalAddress.String(), strconv.Itoa(int(c.LocalPort))) remoteEndpointNodeID = report.MakeEndpointNodeID(r.hostID, c.RemoteAddress.String(), strconv.Itoa(int(c.RemotePort))) - adjecencyID = "" + adjacencyID = "" edgeID = "" ) if localIsClient { - adjecencyID = report.MakeAdjacencyID(localEndpointNodeID) - rpt.Endpoint.Adjacency[adjecencyID] = rpt.Endpoint.Adjacency[adjecencyID].Add(remoteEndpointNodeID) + adjacencyID = report.MakeAdjacencyID(localEndpointNodeID) + rpt.Endpoint.Adjacency[adjacencyID] = rpt.Endpoint.Adjacency[adjacencyID].Add(remoteEndpointNodeID) edgeID = report.MakeEdgeID(localEndpointNodeID, remoteEndpointNodeID) } else { - adjecencyID = report.MakeAdjacencyID(remoteEndpointNodeID) - rpt.Endpoint.Adjacency[adjecencyID] = rpt.Endpoint.Adjacency[adjecencyID].Add(localEndpointNodeID) + adjacencyID = report.MakeAdjacencyID(remoteEndpointNodeID) + rpt.Endpoint.Adjacency[adjacencyID] = rpt.Endpoint.Adjacency[adjacencyID].Add(localEndpointNodeID) edgeID = report.MakeEdgeID(remoteEndpointNodeID, localEndpointNodeID) } diff --git a/render/detailed_node.go b/render/detailed_node.go index 11585a692..2f329228a 100644 --- a/render/detailed_node.go +++ b/render/detailed_node.go @@ -184,25 +184,38 @@ func connectionDetailsRows(topology report.Topology, originID string) []Row { if !ok { return rows } - for _, serverNodeID := range topology.Adjacency[report.MakeAdjacencyID(originID)] { - if remote, ok := labeler(serverNodeID); ok { - rows = append(rows, Row{ - Key: local, - ValueMajor: remote, - }) + // Firstly, collection outgoing connections from this node. + originAdjID := report.MakeAdjacencyID(originID) + for _, serverNodeID := range topology.Adjacency[originAdjID] { + remote, ok := labeler(serverNodeID) + if !ok { + continue } + rows = append(rows, Row{ + Key: local, + ValueMajor: remote, + }) } + // Next, scan the topology for incoming connections to this node. for clientAdjID, serverNodeIDs := range topology.Adjacency { - if serverNodeIDs.Contains(originID) { - if clientNodeID, ok := report.ParseAdjacencyID(clientAdjID); ok { - if remote, ok := labeler(clientNodeID); ok { - rows = append(rows, Row{ - Key: remote, - ValueMajor: local, - }) - } - } + if clientAdjID == originAdjID { + continue } + if !serverNodeIDs.Contains(originID) { + continue + } + clientNodeID, ok := report.ParseAdjacencyID(clientAdjID) + if !ok { + continue + } + remote, ok := labeler(clientNodeID) + if !ok { + continue + } + rows = append(rows, Row{ + Key: remote, + ValueMajor: local, + }) } return rows } diff --git a/render/detailed_node_test.go b/render/detailed_node_test.go index 242f24d56..20b6f97cc 100644 --- a/render/detailed_node_test.go +++ b/render/detailed_node_test.go @@ -94,8 +94,8 @@ func TestMakeDetailedHostNode(t *testing.T) { Rank: 0, Rows: []render.Row{ { - Key: "Local", - ValueMajor: "Remote", + Key: "Client", + ValueMajor: "Server", ValueMinor: "", }, { diff --git a/render/mapping.go b/render/mapping.go index 5e77db13c..8d3369b25 100644 --- a/render/mapping.go +++ b/render/mapping.go @@ -33,12 +33,12 @@ const ( // rendered topology. type LeafMapFunc func(report.NodeMetadata) (RenderableNode, bool) -// PseudoFunc creates RenderableNode representing pseudo nodes given the nodeID. -// dstNodeID is the node id of one of the nodes this node is attached to. -// nodeID and dstNodeID are node IDs prior to mapping. isClient indicated the direction -// of the edge to dstNodeID - true indicates nodeID is the client, false indicates -// nodeID is the server. -type PseudoFunc func(nodeID string, dstNodeID string, isClient bool, local report.Networks) (RenderableNode, bool) +// PseudoFunc creates RenderableNode representing pseudo nodes given the +// srcNodeID. dstNodeID is the node id of one of the nodes this node has an +// edge to. srcNodeID and dstNodeID are node IDs prior to mapping. srcIsClient +// indicates the direction of the edge to dstNodeID - true indicates srcNodeID +// is the client, false indicates dstNodeID is the server. +type PseudoFunc func(srcNodeID, dstNodeID string, srcIsClient bool, local report.Networks) (RenderableNode, bool) // MapFunc is anything which can take an arbitrary RenderableNode and // return another RenderableNode. @@ -324,25 +324,25 @@ func MapAddress2Host(n RenderableNode) (RenderableNode, bool) { // the report's local networks. Otherwise, the returned function will // produce a single pseudo node per (dst address, src address, src port). func GenericPseudoNode(addresser func(id string) net.IP) PseudoFunc { - return func(nodeID, dstNodeId string, isClient bool, local report.Networks) (RenderableNode, bool) { + return func(srcNodeID, dstNodeID string, srcIsClient bool, local report.Networks) (RenderableNode, bool) { // Use the addresser to extract the IP of the missing node - nodeAddr := addresser(nodeID) + srcNodeAddr := addresser(srcNodeID) // If the dstNodeAddr is not in a network local to this report, we emit an // internet node - if !local.Contains(nodeAddr) { + if !local.Contains(srcNodeAddr) { return newPseudoNode(TheInternetID, TheInternetMajor, ""), true } - if isClient { - // If the client node is missing, generate a single pseudo node for every (client ip, server ip, server por) - serverIP, serverPort := trySplitAddr(dstNodeId) - outputID := MakePseudoNodeID(nodeAddr.String(), serverIP, serverPort) - major := nodeAddr.String() + if srcIsClient { + // If the client node is missing, generate a single pseudo node for every (client ip, server ip, server port) + serverIP, serverPort := trySplitAddr(dstNodeID) + outputID := MakePseudoNodeID(srcNodeAddr.String(), serverIP, serverPort) + major := srcNodeAddr.String() return newPseudoNode(outputID, major, ""), true } - // Othereise (the server node is missing), generate a pseudo node for every (server ip, server port) - serverIP, serverPort := trySplitAddr(nodeID) + // Otherwise (the server node is missing), generate a pseudo node for every (server ip, server port) + serverIP, serverPort := trySplitAddr(srcNodeID) outputID := MakePseudoNodeID(serverIP, serverPort) if serverPort != "" { return newPseudoNode(outputID, serverIP+":"+serverPort, ""), true diff --git a/render/render.go b/render/render.go index d9dfcafb4..c6ea9c2f4 100644 --- a/render/render.go +++ b/render/render.go @@ -177,8 +177,8 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes { source2mapped[nodeID] = mapped.ID } - mkPseudoNode := func(srcID, dstId string, srcIsClient bool) (string, bool) { - pseudoNode, ok := m.Pseudo(srcID, dstId, srcIsClient, localNetworks) + mkPseudoNode := func(srcNodeID, dstNodeID string, srcIsClient bool) (string, bool) { + pseudoNode, ok := m.Pseudo(srcNodeID, dstNodeID, srcIsClient, localNetworks) if !ok { return "", false } @@ -191,7 +191,7 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes { } nodes[pseudoNode.ID] = pseudoNode - source2mapped[pseudoNode.ID] = srcID + source2mapped[pseudoNode.ID] = srcNodeID return pseudoNode.ID, true }