From 7fb1a98eca31e3d34ace98de5642a277313ed034 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Mon, 17 Aug 2015 12:46:11 +0000 Subject: [PATCH] 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 } }