From 443e92c11080987a5c4cf4050610f3409ee442b1 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Thu, 27 Aug 2015 14:12:41 +0000 Subject: [PATCH] Move Adjacency info inside the NodeMetadata struct; move pseudo node generation to the probes. --- probe/docker/reporter_test.go | 2 - probe/endpoint/reporter.go | 78 ++++++------- probe/endpoint/reporter_test.go | 18 ++- probe/host/tagger.go | 5 +- probe/host/tagger_test.go | 4 +- probe/overlay/weave_test.go | 1 - probe/process/reporter_test.go | 1 - probe/sniff/sniffer.go | 41 +++---- probe/sniff/sniffer_test.go | 32 ++++-- render/detailed_node.go | 12 +- render/detailed_node_test.go | 8 +- render/expected/expected.go | 12 +- render/mapping.go | 122 +++++++++----------- render/mapping_test.go | 2 +- render/render.go | 76 ++++--------- render/render_test.go | 26 +++-- render/renderable_node_test.go | 5 +- render/topologies.go | 9 -- report/id.go | 36 ------ report/id_list.go | 16 ++- report/id_test.go | 73 ------------ report/merge_test.go | 89 --------------- report/topology.go | 90 ++++----------- test/report_fixture.go | 195 +++++++++++++++++++++----------- 24 files changed, 369 insertions(+), 584 deletions(-) diff --git a/probe/docker/reporter_test.go b/probe/docker/reporter_test.go index dfd4b5e298..8f3b05fdff 100644 --- a/probe/docker/reporter_test.go +++ b/probe/docker/reporter_test.go @@ -50,7 +50,6 @@ var ( func TestReporter(t *testing.T) { want := report.MakeReport() want.Container = report.Topology{ - Adjacency: report.Adjacency{}, EdgeMetadatas: report.EdgeMetadatas{}, NodeMetadatas: report.NodeMetadatas{ report.MakeContainerNodeID("", "ping"): report.MakeNodeMetadataWith(map[string]string{ @@ -61,7 +60,6 @@ func TestReporter(t *testing.T) { }, } want.ContainerImage = report.Topology{ - Adjacency: report.Adjacency{}, EdgeMetadatas: report.EdgeMetadatas{}, NodeMetadatas: report.NodeMetadatas{ report.MakeContainerNodeID("", "baz"): report.MakeNodeMetadataWith(map[string]string{ diff --git a/probe/endpoint/reporter.go b/probe/endpoint/reporter.go index 3a758f9ee5..6043101d5c 100644 --- a/probe/endpoint/reporter.go +++ b/probe/endpoint/reporter.go @@ -125,37 +125,45 @@ func (r *Reporter) Report() (report.Report, error) { } func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr string, localPort, remotePort uint16, proc *procspy.Proc) { - localIsClient := int(localPort) > int(remotePort) + var ( + localIsClient = int(localPort) > int(remotePort) + hostNodeID = report.MakeHostNodeID(r.hostID) + addNode = func(t report.Topology, nodeID string, nmd report.NodeMetadata) { + if existing, ok := t.NodeMetadatas[nodeID]; ok { + nmd = nmd.Merge(existing) + } + t.NodeMetadatas[nodeID] = nmd + } + ) // Update address topology { var ( localAddressNodeID = report.MakeAddressNodeID(r.hostID, localAddr) remoteAddressNodeID = report.MakeAddressNodeID(r.hostID, remoteAddr) - adjacencyID = "" edgeID = "" + + localNode = report.MakeNodeMetadataWith(map[string]string{ + "name": r.hostName, + Addr: localAddr, + report.HostNodeID: hostNodeID, + }) + remoteNode = report.MakeNodeMetadataWith(map[string]string{ + Addr: remoteAddr, + }) ) if localIsClient { - adjacencyID = report.MakeAdjacencyID(localAddressNodeID) - rpt.Address.Adjacency[adjacencyID] = rpt.Address.Adjacency[adjacencyID].Add(remoteAddressNodeID) - + localNode.Adjacency = localNode.Adjacency.Add(remoteAddressNodeID) edgeID = report.MakeEdgeID(localAddressNodeID, remoteAddressNodeID) } else { - adjacencyID = report.MakeAdjacencyID(remoteAddressNodeID) - rpt.Address.Adjacency[adjacencyID] = rpt.Address.Adjacency[adjacencyID].Add(localAddressNodeID) - + remoteNode.Adjacency = localNode.Adjacency.Add(localAddressNodeID) edgeID = report.MakeEdgeID(remoteAddressNodeID, localAddressNodeID) } + addNode(rpt.Address, localAddressNodeID, localNode) + addNode(rpt.Address, remoteAddressNodeID, remoteNode) countTCPConnection(rpt.Address.EdgeMetadatas, edgeID) - - if _, ok := rpt.Address.NodeMetadatas[localAddressNodeID]; !ok { - rpt.Address.NodeMetadatas[localAddressNodeID] = report.MakeNodeMetadataWith(map[string]string{ - "name": r.hostName, - Addr: localAddr, - }) - } } // Update endpoint topology @@ -163,40 +171,34 @@ func (r *Reporter) addConnection(rpt *report.Report, localAddr, remoteAddr strin var ( localEndpointNodeID = report.MakeEndpointNodeID(r.hostID, localAddr, strconv.Itoa(int(localPort))) remoteEndpointNodeID = report.MakeEndpointNodeID(r.hostID, remoteAddr, strconv.Itoa(int(remotePort))) - adjacencyID = "" edgeID = "" + + localNode = report.MakeNodeMetadataWith(map[string]string{ + Addr: localAddr, + Port: strconv.Itoa(int(localPort)), + report.HostNodeID: hostNodeID, + }) + remoteNode = report.MakeNodeMetadataWith(map[string]string{ + Addr: remoteAddr, + Port: strconv.Itoa(int(remotePort)), + }) ) if localIsClient { - adjacencyID = report.MakeAdjacencyID(localEndpointNodeID) - rpt.Endpoint.Adjacency[adjacencyID] = rpt.Endpoint.Adjacency[adjacencyID].Add(remoteEndpointNodeID) - + localNode.Adjacency = localNode.Adjacency.Add(remoteEndpointNodeID) edgeID = report.MakeEdgeID(localEndpointNodeID, remoteEndpointNodeID) } else { - adjacencyID = report.MakeAdjacencyID(remoteEndpointNodeID) - rpt.Endpoint.Adjacency[adjacencyID] = rpt.Endpoint.Adjacency[adjacencyID].Add(localEndpointNodeID) - + remoteNode.Adjacency = remoteNode.Adjacency.Add(localEndpointNodeID) edgeID = report.MakeEdgeID(remoteEndpointNodeID, localEndpointNodeID) } - countTCPConnection(rpt.Endpoint.EdgeMetadatas, edgeID) - - md, ok := rpt.Endpoint.NodeMetadatas[localEndpointNodeID] - updated := !ok - if !ok { - md = report.MakeNodeMetadataWith(map[string]string{ - Addr: localAddr, - Port: strconv.Itoa(int(localPort)), - }) - } if proc != nil && proc.PID > 0 { - pid := strconv.FormatUint(uint64(proc.PID), 10) - updated = updated || md.Metadata[process.PID] != pid - md.Metadata[process.PID] = pid - } - if updated { - rpt.Endpoint.NodeMetadatas[localEndpointNodeID] = md + localNode.Metadata[process.PID] = strconv.FormatUint(uint64(proc.PID), 10) } + + addNode(rpt.Endpoint, localEndpointNodeID, localNode) + addNode(rpt.Endpoint, remoteEndpointNodeID, remoteNode) + countTCPConnection(rpt.Endpoint.EdgeMetadatas, edgeID) } } diff --git a/probe/endpoint/reporter_test.go b/probe/endpoint/reporter_test.go index 0258c16360..05b837425f 100644 --- a/probe/endpoint/reporter_test.go +++ b/probe/endpoint/reporter_test.go @@ -77,25 +77,24 @@ func TestSpyNoProcesses(t *testing.T) { //t.Logf("\n%s\n", buf) // No process nodes, please - if want, have := 0, len(r.Endpoint.Adjacency); want != have { + if want, have := 0, len(r.Endpoint.NodeMetadatas); want != have { t.Fatalf("want %d, have %d", want, have) } var ( scopedLocal = report.MakeAddressNodeID(nodeID, fixLocalAddress.String()) scopedRemote = report.MakeAddressNodeID(nodeID, fixRemoteAddress.String()) - remoteKey = report.MakeAdjacencyID(scopedRemote) ) - if want, have := 1, len(r.Address.Adjacency[remoteKey]); want != have { - t.Fatalf("want %d, have %d", want, have) + if want, have := nodeName, r.Address.NodeMetadatas[scopedLocal].Metadata[docker.Name]; want != have { + t.Fatalf("want %q, have %q", want, have) } - if want, have := scopedLocal, r.Address.Adjacency[remoteKey][0]; want != have { - t.Fatalf("want %q, have %q", want, have) + if want, have := 1, len(r.Address.NodeMetadatas[scopedRemote].Adjacency); want != have { + t.Fatalf("want %d, have %d", want, have) } - if want, have := nodeName, r.Address.NodeMetadatas[scopedLocal].Metadata[docker.Name]; want != have { + if want, have := scopedLocal, r.Address.NodeMetadatas[scopedRemote].Adjacency[0]; want != have { t.Fatalf("want %q, have %q", want, have) } } @@ -115,14 +114,13 @@ 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))) - remoteKey = report.MakeAdjacencyID(scopedRemote) ) - if want, have := 1, len(r.Endpoint.Adjacency[remoteKey]); want != have { + if want, have := 1, len(r.Endpoint.NodeMetadatas[scopedRemote].Adjacency); want != have { t.Fatalf("want %d, have %d", want, have) } - if want, have := scopedLocal, r.Endpoint.Adjacency[remoteKey][0]; want != have { + if want, have := scopedLocal, r.Endpoint.NodeMetadatas[scopedRemote].Adjacency[0]; want != have { t.Fatalf("want %q, have %q", want, have) } diff --git a/probe/host/tagger.go b/probe/host/tagger.go index ac381012eb..1bbfde112f 100644 --- a/probe/host/tagger.go +++ b/probe/host/tagger.go @@ -18,7 +18,10 @@ func NewTagger(hostID string) Tagger { // Tag implements Tagger. func (t Tagger) Tag(r report.Report) (report.Report, error) { other := report.MakeNodeMetadataWith(map[string]string{report.HostNodeID: t.hostNodeID}) - for _, topology := range r.Topologies() { + + // Explicity don't tag Endpoints and Addresses - These topologies include pseudo nodes, + // and as such do their own host tagging + for _, topology := range []report.Topology{r.Process, r.Container, r.ContainerImage, r.Host, r.Overlay} { for id, md := range topology.NodeMetadatas { topology.NodeMetadatas[id] = md.Merge(other) } diff --git a/probe/host/tagger_test.go b/probe/host/tagger_test.go index cc4200e208..97abefcc82 100644 --- a/probe/host/tagger_test.go +++ b/probe/host/tagger_test.go @@ -17,12 +17,12 @@ func TestTagger(t *testing.T) { ) r := report.MakeReport() - r.Endpoint.NodeMetadatas[endpointNodeID] = nodeMetadata + r.Process.NodeMetadatas[endpointNodeID] = nodeMetadata want := nodeMetadata.Merge(report.MakeNodeMetadataWith(map[string]string{ report.HostNodeID: report.MakeHostNodeID(hostID), })) rpt, _ := host.NewTagger(hostID).Tag(r) - have := rpt.Endpoint.NodeMetadatas[endpointNodeID].Copy() + have := rpt.Process.NodeMetadatas[endpointNodeID].Copy() if !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } diff --git a/probe/overlay/weave_test.go b/probe/overlay/weave_test.go index 7ea89ee76a..b9e33f0745 100644 --- a/probe/overlay/weave_test.go +++ b/probe/overlay/weave_test.go @@ -36,7 +36,6 @@ func TestWeaveTaggerOverlayTopology(t *testing.T) { t.Fatal(err) } if want, have := (report.Topology{ - Adjacency: report.Adjacency{}, EdgeMetadatas: report.EdgeMetadatas{}, NodeMetadatas: report.NodeMetadatas{ report.MakeOverlayNodeID(mockWeavePeerName): report.MakeNodeMetadataWith(map[string]string{ diff --git a/probe/process/reporter_test.go b/probe/process/reporter_test.go index 1e4bf4858a..fa4d3f30ec 100644 --- a/probe/process/reporter_test.go +++ b/probe/process/reporter_test.go @@ -34,7 +34,6 @@ func TestReporter(t *testing.T) { reporter := process.NewReporter(walker, "") want := report.MakeReport() want.Process = report.Topology{ - Adjacency: report.Adjacency{}, EdgeMetadatas: report.EdgeMetadatas{}, NodeMetadatas: report.NodeMetadatas{ report.MakeProcessNodeID("", "1"): report.MakeNodeMetadataWith(map[string]string{ diff --git a/probe/sniff/sniffer.go b/probe/sniff/sniffer.go index 70209f7e84..e45a205079 100644 --- a/probe/sniff/sniffer.go +++ b/probe/sniff/sniffer.go @@ -250,21 +250,30 @@ func (s *Sniffer) Merge(p Packet, rpt report.Report) { return } + addAdjacency := func(t report.Topology, srcNodeID, dstNodeID string) { + srcNode, ok := t.NodeMetadatas[srcNodeID] + if !ok { + srcNode = report.MakeNodeMetadata() + } + srcNode.Adjacency = srcNode.Adjacency.Add(dstNodeID) + t.NodeMetadatas[srcNodeID] = srcNode + + if _, ok := t.NodeMetadatas[dstNodeID]; !ok { + t.NodeMetadatas[dstNodeID] = report.MakeNodeMetadata() + } + } + // For sure, we can add to the address topology. { var ( - srcNodeID = report.MakeAddressNodeID(s.hostID, localIP) - dstNodeID = report.MakeAddressNodeID(s.hostID, remoteIP) - edgeID = report.MakeEdgeID(srcNodeID, dstNodeID) - srcAdjacencyID = report.MakeAdjacencyID(srcNodeID) + srcNodeID = report.MakeAddressNodeID(s.hostID, localIP) + dstNodeID = report.MakeAddressNodeID(s.hostID, remoteIP) + edgeID = report.MakeEdgeID(srcNodeID, dstNodeID) ) - if _, ok := rpt.Address.NodeMetadatas[srcNodeID]; !ok { - rpt.Address.NodeMetadatas[srcNodeID] = report.MakeNodeMetadata() - } + addAdjacency(rpt.Address, srcNodeID, dstNodeID) emd := rpt.Address.EdgeMetadatas[edgeID] - if egress { if emd.EgressPacketCount == nil { emd.EgressPacketCount = new(uint64) @@ -284,26 +293,20 @@ func (s *Sniffer) Merge(p Packet, rpt report.Report) { } *emd.IngressByteCount += uint64(p.Network) } - rpt.Address.EdgeMetadatas[edgeID] = emd - rpt.Address.Adjacency[srcAdjacencyID] = rpt.Address.Adjacency[srcAdjacencyID].Add(dstNodeID) } // If we have ports, we can add to the endpoint topology, too. if p.SrcPort != "" && p.DstPort != "" { var ( - srcNodeID = report.MakeEndpointNodeID(s.hostID, localIP, localPort) - dstNodeID = report.MakeEndpointNodeID(s.hostID, remoteIP, remotePort) - edgeID = report.MakeEdgeID(srcNodeID, dstNodeID) - srcAdjacencyID = report.MakeAdjacencyID(srcNodeID) + srcNodeID = report.MakeEndpointNodeID(s.hostID, localIP, localPort) + dstNodeID = report.MakeEndpointNodeID(s.hostID, remoteIP, remotePort) + edgeID = report.MakeEdgeID(srcNodeID, dstNodeID) ) - if _, ok := rpt.Endpoint.NodeMetadatas[srcNodeID]; !ok { - rpt.Endpoint.NodeMetadatas[srcNodeID] = report.MakeNodeMetadata() - } + addAdjacency(rpt.Endpoint, srcNodeID, dstNodeID) emd := rpt.Endpoint.EdgeMetadatas[edgeID] - if egress { if emd.EgressPacketCount == nil { emd.EgressPacketCount = new(uint64) @@ -323,8 +326,6 @@ func (s *Sniffer) Merge(p Packet, rpt report.Report) { } *emd.IngressByteCount += uint64(p.Transport) } - rpt.Endpoint.EdgeMetadatas[edgeID] = emd - rpt.Endpoint.Adjacency[srcAdjacencyID] = rpt.Endpoint.Adjacency[srcAdjacencyID].Add(dstNodeID) } } diff --git a/probe/sniff/sniffer_test.go b/probe/sniff/sniffer_test.go index d939ee5eeb..8d888cb589 100644 --- a/probe/sniff/sniffer_test.go +++ b/probe/sniff/sniffer_test.go @@ -65,11 +65,6 @@ func TestMerge(t *testing.T) { dstEndpointNodeID = report.MakeEndpointNodeID(hostID, p.DstIP, p.DstPort) ) if want, have := (report.Topology{ - Adjacency: report.Adjacency{ - report.MakeAdjacencyID(srcEndpointNodeID): report.MakeIDList( - dstEndpointNodeID, - ), - }, EdgeMetadatas: report.EdgeMetadatas{ report.MakeEdgeID(srcEndpointNodeID, dstEndpointNodeID): report.EdgeMetadata{ EgressPacketCount: newu64(1), @@ -77,7 +72,16 @@ func TestMerge(t *testing.T) { }, }, NodeMetadatas: report.NodeMetadatas{ - srcEndpointNodeID: report.MakeNodeMetadata(), + srcEndpointNodeID: { + Metadata: map[string]string{}, + Counters: map[string]int{}, + Adjacency: report.MakeIDList(dstEndpointNodeID), + }, + dstEndpointNodeID: report.NodeMetadata{ + Metadata: map[string]string{}, + Counters: map[string]int{}, + Adjacency: report.MakeIDList(), + }, }, }), rpt.Endpoint; !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) @@ -88,11 +92,6 @@ func TestMerge(t *testing.T) { dstAddressNodeID = report.MakeAddressNodeID(hostID, p.DstIP) ) if want, have := (report.Topology{ - Adjacency: report.Adjacency{ - report.MakeAdjacencyID(srcAddressNodeID): report.MakeIDList( - dstAddressNodeID, - ), - }, EdgeMetadatas: report.EdgeMetadatas{ report.MakeEdgeID(srcAddressNodeID, dstAddressNodeID): report.EdgeMetadata{ EgressPacketCount: newu64(1), @@ -100,7 +99,16 @@ func TestMerge(t *testing.T) { }, }, NodeMetadatas: report.NodeMetadatas{ - srcAddressNodeID: report.MakeNodeMetadata(), + srcAddressNodeID: report.NodeMetadata{ + Metadata: map[string]string{}, + Counters: map[string]int{}, + Adjacency: report.MakeIDList(dstAddressNodeID), + }, + dstAddressNodeID: report.NodeMetadata{ + Metadata: map[string]string{}, + Counters: map[string]int{}, + Adjacency: report.MakeIDList(), + }, }, }), rpt.Address; !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) diff --git a/render/detailed_node.go b/render/detailed_node.go index 8a1327b031..2899a66357 100644 --- a/render/detailed_node.go +++ b/render/detailed_node.go @@ -219,8 +219,7 @@ func connectionDetailsRows(topology report.Topology, originID string) []Row { return rows } // Firstly, collection outgoing connections from this node. - originAdjID := report.MakeAdjacencyID(originID) - for _, serverNodeID := range topology.Adjacency[originAdjID] { + for _, serverNodeID := range topology.NodeMetadatas[originID].Adjacency { remote, ok := labeler(serverNodeID) if !ok { continue @@ -232,17 +231,14 @@ func connectionDetailsRows(topology report.Topology, originID string) []Row { }) } // Next, scan the topology for incoming connections to this node. - for clientAdjID, serverNodeIDs := range topology.Adjacency { - if clientAdjID == originAdjID { + for clientNodeID, clientNodeMetadata := range topology.NodeMetadatas { + if clientNodeID == originID { continue } + serverNodeIDs := clientNodeMetadata.Adjacency if !serverNodeIDs.Contains(originID) { continue } - clientNodeID, ok := report.ParseAdjacencyID(clientAdjID) - if !ok { - continue - } remote, ok := labeler(clientNodeID) if !ok { continue diff --git a/render/detailed_node_test.go b/render/detailed_node_test.go index f47c540641..0fa7499c92 100644 --- a/render/detailed_node_test.go +++ b/render/detailed_node_test.go @@ -176,19 +176,19 @@ func TestMakeDetailedContainerNode(t *testing.T) { {"Egress byte rate", "1.0", "KBps", false}, {"Client", "Server", "", true}, { - fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010), + fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.UnknownClient1Port), fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), "", true, }, { - fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020), + fmt.Sprintf("%s:%s", test.UnknownClient2IP, test.UnknownClient2Port), fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), "", true, }, { - fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020), + fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.UnknownClient3Port), fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), "", true, @@ -206,7 +206,7 @@ func TestMakeDetailedContainerNode(t *testing.T) { true, }, { - fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345), + fmt.Sprintf("%s:%s", test.RandomClientIP, test.RandomClientPort), fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort), "", true, diff --git a/render/expected/expected.go b/render/expected/expected.go index 1b2ffb01ba..d90cd9ba9b 100644 --- a/render/expected/expected.go +++ b/render/expected/expected.go @@ -323,8 +323,8 @@ var ( ServerHostRenderedID = render.MakeHostID(test.ServerHostID) ClientHostRenderedID = render.MakeHostID(test.ClientHostID) - pseudoHostID1 = render.MakePseudoNodeID("10.10.10.10", "192.168.1.1", "") - pseudoHostID2 = render.MakePseudoNodeID("10.10.10.11", "192.168.1.1", "") + pseudoHostID1 = render.MakePseudoNodeID(test.UnknownClient1IP, test.ServerIP) + pseudoHostID2 = render.MakePseudoNodeID(test.UnknownClient3IP, test.ServerIP) RenderedHosts = render.RenderableNodes{ ServerHostRenderedID: { @@ -361,21 +361,21 @@ var ( }, pseudoHostID1: { ID: pseudoHostID1, - LabelMajor: "10.10.10.10", + LabelMajor: test.UnknownClient1IP, Pseudo: true, Adjacency: report.MakeIDList(ServerHostRenderedID), NodeMetadata: report.MakeNodeMetadata(), EdgeMetadata: report.EdgeMetadata{}, - Origins: report.MakeIDList(test.UnknownAddress1NodeID), + Origins: report.MakeIDList(test.UnknownAddress1NodeID, test.UnknownAddress2NodeID), }, pseudoHostID2: { ID: pseudoHostID2, - LabelMajor: "10.10.10.11", + LabelMajor: test.UnknownClient3IP, Pseudo: true, Adjacency: report.MakeIDList(ServerHostRenderedID), NodeMetadata: report.MakeNodeMetadata(), EdgeMetadata: report.EdgeMetadata{}, - Origins: report.MakeIDList(test.UnknownAddress2NodeID), + Origins: report.MakeIDList(test.UnknownAddress3NodeID), }, render.TheInternetID: { ID: render.TheInternetID, diff --git a/render/mapping.go b/render/mapping.go index 691817f38e..ce00049dbc 100644 --- a/render/mapping.go +++ b/render/mapping.go @@ -3,6 +3,7 @@ package render import ( "fmt" "net" + "strconv" "strings" "github.com/weaveworks/scope/probe/docker" @@ -31,14 +32,7 @@ const ( // // A single NodeMetadata can yield arbitrary many representations, including // representations that reduce (or even increase) the cardinality of the set of nodes. -type LeafMapFunc func(report.NodeMetadata) RenderableNodes - -// 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) +type LeafMapFunc func(report.NodeMetadata, report.Networks) RenderableNodes // MapFunc is anything which can take an arbitrary RenderableNode and // return a set of other RenderableNodes. @@ -50,7 +44,7 @@ type MapFunc func(RenderableNode) RenderableNodes // MapEndpointIdentity maps an endpoint topology node to a single endpoint // renderable node. As it is only ever run on endpoint topology nodes, we // expect that certain keys are present. -func MapEndpointIdentity(m report.NodeMetadata) RenderableNodes { +func MapEndpointIdentity(m report.NodeMetadata, local report.Networks) RenderableNodes { addr, ok := m.Metadata[endpoint.Addr] if !ok { return RenderableNodes{} @@ -61,6 +55,34 @@ func MapEndpointIdentity(m report.NodeMetadata) RenderableNodes { return RenderableNodes{} } + // Nodes without a hostid are treated as psuedo nodes + _, ok = m.Metadata[report.HostNodeID] + if !ok { + // If the dstNodeAddr is not in a network local to this report, we emit an + // internet node + if !local.Contains(net.ParseIP(addr)) { + return RenderableNodes{TheInternetID: newPseudoNode(TheInternetID, TheInternetMajor, "")} + } + + // We are a 'client' pseudo node if the port is in the ephemeral port range. + // Linux uses 32768 to 61000. + if p, err := strconv.Atoi(port); err == nil && p >= 32768 && p < 61000 { + // We only exist if there is something in our adjacency + // Generate a single pseudo node for every (client ip, server ip, server port) + dstNodeID := m.Adjacency[0] + serverIP, serverPort := trySplitAddr(dstNodeID) + outputID := MakePseudoNodeID(addr, serverIP, serverPort) + return RenderableNodes{outputID: newPseudoNode(outputID, addr, "")} + } + + // Otherwise (the server node is missing), generate a pseudo node for every (server ip, server port) + outputID := MakePseudoNodeID(addr, port) + if port != "" { + return RenderableNodes{outputID: newPseudoNode(outputID, addr+":"+port, "")} + } + return RenderableNodes{outputID: newPseudoNode(outputID, addr, "")} + } + var ( id = MakeEndpointID(report.ExtractHostID(m), addr, port) major = fmt.Sprintf("%s:%s", addr, port) @@ -78,7 +100,7 @@ func MapEndpointIdentity(m report.NodeMetadata) RenderableNodes { // MapProcessIdentity maps a process topology node to a process renderable // node. As it is only ever run on process topology nodes, we expect that // certain keys are present. -func MapProcessIdentity(m report.NodeMetadata) RenderableNodes { +func MapProcessIdentity(m report.NodeMetadata, _ report.Networks) RenderableNodes { pid, ok := m.Metadata[process.PID] if !ok { return RenderableNodes{} @@ -97,7 +119,7 @@ func MapProcessIdentity(m report.NodeMetadata) RenderableNodes { // MapContainerIdentity maps a container topology node to a container // renderable node. As it is only ever run on container topology nodes, we // expect that certain keys are present. -func MapContainerIdentity(m report.NodeMetadata) RenderableNodes { +func MapContainerIdentity(m report.NodeMetadata, _ report.Networks) RenderableNodes { id, ok := m.Metadata[docker.ContainerID] if !ok { return RenderableNodes{} @@ -115,7 +137,7 @@ func MapContainerIdentity(m report.NodeMetadata) RenderableNodes { // MapContainerImageIdentity maps a container image topology node to container // image renderable node. As it is only ever run on container image topology // nodes, we expect that certain keys are present. -func MapContainerImageIdentity(m report.NodeMetadata) RenderableNodes { +func MapContainerImageIdentity(m report.NodeMetadata, _ report.Networks) RenderableNodes { id, ok := m.Metadata[docker.ImageID] if !ok { return RenderableNodes{} @@ -132,12 +154,27 @@ func MapContainerImageIdentity(m report.NodeMetadata) RenderableNodes { // MapAddressIdentity maps an address topology node to an address renderable // node. As it is only ever run on address topology nodes, we expect that // certain keys are present. -func MapAddressIdentity(m report.NodeMetadata) RenderableNodes { +func MapAddressIdentity(m report.NodeMetadata, local report.Networks) RenderableNodes { addr, ok := m.Metadata[endpoint.Addr] if !ok { return RenderableNodes{} } + // Nodes without a hostid are treated as psuedo nodes + _, ok = m.Metadata[report.HostNodeID] + if !ok { + // If the addr is not in a network local to this report, we emit an + // internet node + if !local.Contains(net.ParseIP(addr)) { + return RenderableNodes{TheInternetID: newPseudoNode(TheInternetID, TheInternetMajor, "")} + } + + // Otherwise generate a pseudo node for every + _, dstID, _ := report.ParseAddressNodeID(m.Adjacency[0]) + outputID := MakePseudoNodeID(addr, dstID) + return RenderableNodes{outputID: newPseudoNode(outputID, addr, "")} + } + var ( id = MakeAddressID(report.ExtractHostID(m), addr) major = addr @@ -151,7 +188,7 @@ func MapAddressIdentity(m report.NodeMetadata) RenderableNodes { // MapHostIdentity maps a host topology node to a host renderable node. As it // is only ever run on host topology nodes, we expect that certain keys are // present. -func MapHostIdentity(m report.NodeMetadata) RenderableNodes { +func MapHostIdentity(m report.NodeMetadata, _ report.Networks) RenderableNodes { var ( id = MakeHostID(report.ExtractHostID(m)) hostname = m.Metadata[host.HostName] @@ -172,7 +209,7 @@ func MapHostIdentity(m report.NodeMetadata) RenderableNodes { // with container nodes. We drop endpoint nodes with pids, as they // will be joined to containers through the process topology, and we // don't want to double count edges. -func MapEndpoint2IP(m report.NodeMetadata) RenderableNodes { +func MapEndpoint2IP(m report.NodeMetadata, local report.Networks) RenderableNodes { _, ok := m.Metadata[process.PID] if ok { return RenderableNodes{} @@ -181,26 +218,16 @@ func MapEndpoint2IP(m report.NodeMetadata) RenderableNodes { if !ok { return RenderableNodes{} } + if !local.Contains(net.ParseIP(addr)) { + return RenderableNodes{TheInternetID: newPseudoNode(TheInternetID, TheInternetMajor, "")} + } return RenderableNodes{addr: NewRenderableNode(addr, "", "", "", m)} } -// IPPseudoNode maps endpoint pseudo nodes to regular IP address nodes, or -// the internet node. -func IPPseudoNode(srcNodeID, _ string, _ bool, local report.Networks) (RenderableNode, bool) { - // Use the addresser to extract the IP of the missing node - srcNodeAddr := report.EndpointIDAddresser(srcNodeID) - // If the dstNodeAddr is not in a network local to this report, we emit an - // internet node - if !local.Contains(srcNodeAddr) { - return newPseudoNode(TheInternetID, TheInternetMajor, ""), true - } - return NewRenderableNode(srcNodeAddr.String(), "", "", "", report.MakeNodeMetadata()), true -} - // MapContainer2IP maps container nodes to their IP addresses (outputs // multiple nodes). This allows container to be joined directly with // the endpoint topology. -func MapContainer2IP(m report.NodeMetadata) RenderableNodes { +func MapContainer2IP(m report.NodeMetadata, _ report.Networks) RenderableNodes { result := RenderableNodes{} addrs, ok := m.Metadata[docker.ContainerIPs] if !ok { @@ -430,43 +457,6 @@ func MapAddress2Host(n RenderableNode) RenderableNodes { return RenderableNodes{id: newDerivedNode(id, n)} } -// GenericPseudoNode makes a PseudoFunc given an addresser. The returned -// PseudoFunc will produce Internet pseudo nodes for addresses not in -// 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(srcNodeID, dstNodeID string, srcIsClient bool, local report.Networks) (RenderableNode, bool) { - // Use the addresser to extract the IP of the missing node - srcNodeAddr := addresser(srcNodeID) - // If the dstNodeAddr is not in a network local to this report, we emit an - // internet node - if !local.Contains(srcNodeAddr) { - return newPseudoNode(TheInternetID, TheInternetMajor, ""), true - } - - 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 - } - - // 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 - } - return newPseudoNode(outputID, serverIP, ""), true - } -} - -// PanicPseudoNode just panics; it is for Topologies without edges -func PanicPseudoNode(src, dst string, isClient bool, local report.Networks) (RenderableNode, bool) { - panic(src) -} - // trySplitAddr is basically ParseArbitraryNodeID, since its callsites // (pseudo funcs) just have opaque node IDs and don't know what topology they // come from. Without changing how pseudo funcs work, we can't make it much diff --git a/render/mapping_test.go b/render/mapping_test.go index 35f746ebe4..9448ecaf28 100644 --- a/render/mapping_test.go +++ b/render/mapping_test.go @@ -72,7 +72,7 @@ type testcase struct { } func testMap(t *testing.T, f render.LeafMapFunc, input testcase) { - if have := f(input.md); input.ok != (len(have) > 0) { + if have := f(input.md, report.Networks{}); input.ok != (len(have) > 0) { t.Errorf("%v: want %v, have %v", input.md, input.ok, have) } } diff --git a/render/render.go b/render/render.go index d1db1ed9ce..7371b16aa4 100644 --- a/render/render.go +++ b/render/render.go @@ -130,7 +130,6 @@ func (m Map) EdgeMetadata(rpt report.Report, srcRenderableID, dstRenderableID st type LeafMap struct { Selector report.TopologySelector Mapper LeafMapFunc - Pseudo PseudoFunc } // Render transforms a given Report into a set of RenderableNodes, which @@ -142,15 +141,16 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes { var ( t = m.Selector(rpt) nodes = RenderableNodes{} + source2mapped = map[string]report.IDList{} // input node ID -> output node IDs + adjacencies = map[string]report.IDList{} // input node ID -> input node Adjacencies localNetworks = LocalNetworks(rpt) ) // Build a set of RenderableNodes for all non-pseudo probes, and an // addressID to nodeID lookup map. Multiple addressIDs can map to the same // RenderableNodes. - source2mapped := map[string]report.IDList{} // source node ID -> mapped node IDs for nodeID, metadata := range t.NodeMetadatas { - for _, mapped := range m.Mapper(metadata) { + for _, mapped := range m.Mapper(metadata, localNetworks) { // mapped.ID needs not be unique over all addressIDs. If not, we merge with // the existing data, on the assumption that the MapFunc returns the same // data. @@ -161,66 +161,25 @@ func (m LeafMap) Render(rpt report.Report) RenderableNodes { origins := mapped.Origins origins = origins.Add(nodeID) - origins = origins.Add(metadata.Metadata[report.HostNodeID]) + if hostNodeID, ok := metadata.Metadata[report.HostNodeID]; ok { + origins = origins.Add(hostNodeID) + } mapped.Origins = origins - nodes[mapped.ID] = mapped source2mapped[nodeID] = source2mapped[nodeID].Add(mapped.ID) + adjacencies[nodeID] = metadata.Adjacency } } - mkPseudoNode := func(srcNodeID, dstNodeID string, srcIsClient bool) report.IDList { - pseudoNode, ok := m.Pseudo(srcNodeID, dstNodeID, srcIsClient, localNetworks) - if !ok { - return report.MakeIDList() - } - pseudoNode.Origins = pseudoNode.Origins.Add(srcNodeID) - existing, ok := nodes[pseudoNode.ID] - if ok { - pseudoNode.Merge(existing) - } - - nodes[pseudoNode.ID] = pseudoNode - source2mapped[pseudoNode.ID] = source2mapped[pseudoNode.ID].Add(srcNodeID) - return report.MakeIDList(pseudoNode.ID) - } - // Walk the graph and make connections. - for src, dsts := range t.Adjacency { - srcNodeID, ok := report.ParseAdjacencyID(src) - if !ok { - log.Printf("bad adjacency ID %q", src) - continue - } - - srcRenderableIDs, ok := source2mapped[srcNodeID] - if !ok { - // One of the entries in dsts must be a non-pseudo node, unless - // it was dropped by the mapping function. - for _, dstNodeID := range dsts { - if _, ok := source2mapped[dstNodeID]; ok { - srcRenderableIDs = mkPseudoNode(srcNodeID, dstNodeID, true) - break - } - } - } - if len(srcRenderableIDs) == 0 { - continue - } - - for _, srcRenderableID := range srcRenderableIDs { + for srcNodeID, dstNodeIDs := range adjacencies { + for _, srcRenderableID := range source2mapped[srcNodeID] { srcRenderableNode := nodes[srcRenderableID] - for _, dstNodeID := range dsts { - dstRenderableIDs, ok := source2mapped[dstNodeID] - if !ok { - dstRenderableIDs = mkPseudoNode(dstNodeID, srcNodeID, false) - } - if len(dstRenderableIDs) == 0 { - continue - } - for _, dstRenderableID := range dstRenderableIDs { + for _, dstNodeID := range dstNodeIDs { + for _, dstRenderableID := range source2mapped[dstNodeID] { dstRenderableNode := nodes[dstRenderableID] + srcRenderableNode.Adjacency = srcRenderableNode.Adjacency.Add(dstRenderableID) // We propagate edge metadata to nodes on both ends of the edges. @@ -253,8 +212,11 @@ func ids(nodes RenderableNodes) report.IDList { // level, it uses the supplied mapping function to translate address IDs to // renderable node (mapped) IDs. func (m LeafMap) EdgeMetadata(rpt report.Report, srcRenderableID, dstRenderableID string) report.EdgeMetadata { - t := m.Selector(rpt) - metadata := report.EdgeMetadata{} + var ( + t = m.Selector(rpt) + localNetworks = LocalNetworks(rpt) + metadata = report.EdgeMetadata{} + ) for edgeID, edgeMeta := range t.EdgeMetadatas { src, dst, ok := report.ParseEdgeID(edgeID) if !ok { @@ -263,11 +225,11 @@ func (m LeafMap) EdgeMetadata(rpt report.Report, srcRenderableID, dstRenderableI } srcs, dsts := report.MakeIDList(src), report.MakeIDList(dst) if src != report.TheInternet { - mapped := m.Mapper(t.NodeMetadatas[src]) + mapped := m.Mapper(t.NodeMetadatas[src], localNetworks) srcs = ids(mapped) } if dst != report.TheInternet { - mapped := m.Mapper(t.NodeMetadatas[dst]) + mapped := m.Mapper(t.NodeMetadatas[dst], localNetworks) dsts = ids(mapped) } if srcs.Contains(srcRenderableID) && dsts.Contains(dstRenderableID) { diff --git a/render/render_test.go b/render/render_test.go index a9f0a5fd49..ddbfa44ab7 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -112,12 +112,14 @@ func TestMapEdge(t *testing.T) { selector := func(_ report.Report) report.Topology { return report.Topology{ NodeMetadatas: report.NodeMetadatas{ - "foo": report.MakeNodeMetadataWith(map[string]string{"id": "foo"}), - "bar": report.MakeNodeMetadataWith(map[string]string{"id": "bar"}), - }, - Adjacency: report.Adjacency{ - ">foo": report.MakeIDList("bar"), - ">bar": report.MakeIDList("foo"), + "foo": { + Metadata: map[string]string{"id": "foo"}, + Adjacency: report.MakeIDList("bar"), + }, + "bar": { + Metadata: map[string]string{"id": "bar"}, + Adjacency: report.MakeIDList("foo"), + }, }, EdgeMetadatas: report.EdgeMetadatas{ "foo|bar": report.EdgeMetadata{EgressPacketCount: newu64(1), EgressByteCount: newu64(2)}, @@ -126,7 +128,7 @@ func TestMapEdge(t *testing.T) { } } - identity := func(nmd report.NodeMetadata) render.RenderableNodes { + identity := func(nmd report.NodeMetadata, _ report.Networks) render.RenderableNodes { return render.RenderableNodes{nmd.Metadata["id"]: render.NewRenderableNode(nmd.Metadata["id"], "", "", "", nmd)} } @@ -138,10 +140,18 @@ func TestMapEdge(t *testing.T) { Renderer: render.LeafMap{ Selector: selector, Mapper: identity, - Pseudo: nil, }, } + have := mapper.Render(report.MakeReport()) + want := render.RenderableNodes{ + "_foo": {ID: "_foo", Adjacency: report.MakeIDList("_bar")}, + "_bar": {ID: "_bar", Adjacency: report.MakeIDList("_foo")}, + } + if !reflect.DeepEqual(want, have) { + t.Error(test.Diff(want, have)) + } + if want, have := (report.EdgeMetadata{ EgressPacketCount: newu64(1), EgressByteCount: newu64(2), diff --git a/render/renderable_node_test.go b/render/renderable_node_test.go index f543622967..4901ec65c9 100644 --- a/render/renderable_node_test.go +++ b/render/renderable_node_test.go @@ -44,7 +44,10 @@ func sterilize(r render.RenderableNodes, destructive bool) render.RenderableNode // case where we explicitly don't compare node metadata. for id, n := range r { if n.Adjacency == nil { - n.Adjacency = report.IDList{} + n.Adjacency = report.MakeIDList() + } + if destructive || n.NodeMetadata.Adjacency == nil { + n.NodeMetadata.Adjacency = report.MakeIDList() } if destructive || n.NodeMetadata.Metadata == nil { n.NodeMetadata.Metadata = map[string]string{} diff --git a/render/topologies.go b/render/topologies.go index 99c83f1083..7d7aab8098 100644 --- a/render/topologies.go +++ b/render/topologies.go @@ -12,7 +12,6 @@ import ( var EndpointRenderer = LeafMap{ Selector: report.SelectEndpoint, Mapper: MapEndpointIdentity, - Pseudo: GenericPseudoNode(report.EndpointIDAddresser), } // ProcessRenderer is a Renderer which produces a renderable process @@ -25,7 +24,6 @@ var ProcessRenderer = MakeReduce( LeafMap{ Selector: report.SelectProcess, Mapper: MapProcessIdentity, - Pseudo: PanicPseudoNode, }, ) @@ -40,7 +38,6 @@ func (r ProcessWithContainerNameRenderer) Render(rpt report.Report) RenderableNo containers := LeafMap{ Selector: report.SelectContainer, Mapper: MapContainerIdentity, - Pseudo: PanicPseudoNode, }.Render(rpt) for id, p := range processes { @@ -103,7 +100,6 @@ var ContainerRenderer = MakeReduce( LeafMap{ Selector: report.SelectContainer, Mapper: MapContainerIdentity, - Pseudo: PanicPseudoNode, }, // This mapper brings in short lived connections by joining with container IPs. @@ -117,12 +113,10 @@ var ContainerRenderer = MakeReduce( LeafMap{ Selector: report.SelectContainer, Mapper: MapContainer2IP, - Pseudo: PanicPseudoNode, }, LeafMap{ Selector: report.SelectEndpoint, Mapper: MapEndpoint2IP, - Pseudo: IPPseudoNode, }, ), ), @@ -143,7 +137,6 @@ var ContainerImageRenderer = Map{ LeafMap{ Selector: report.SelectContainerImage, Mapper: MapContainerImageIdentity, - Pseudo: PanicPseudoNode, }, ), }, @@ -154,7 +147,6 @@ var ContainerImageRenderer = Map{ var AddressRenderer = LeafMap{ Selector: report.SelectAddress, Mapper: MapAddressIdentity, - Pseudo: GenericPseudoNode(report.AddressIDAddresser), } // HostRenderer is a Renderer which produces a renderable host @@ -167,6 +159,5 @@ var HostRenderer = MakeReduce( LeafMap{ Selector: report.SelectHost, Mapper: MapHostIdentity, - Pseudo: PanicPseudoNode, }, ) diff --git a/report/id.go b/report/id.go index a2a88de955..f7adad8cc0 100644 --- a/report/id.go +++ b/report/id.go @@ -21,19 +21,6 @@ const ( EdgeDelim = "|" ) -// MakeAdjacencyID produces an adjacency ID from a node id. -func MakeAdjacencyID(srcNodeID string) string { - return ">" + srcNodeID -} - -// ParseAdjacencyID produces a node ID from an adjancency ID. -func ParseAdjacencyID(adjacencyID string) (string, bool) { - if !strings.HasPrefix(adjacencyID, ">") { - return "", false - } - return adjacencyID[1:], true -} - // MakeEdgeID produces an edge ID from composite parts. func MakeEdgeID(srcNodeID, dstNodeID string) string { return srcNodeID + EdgeDelim + dstNodeID @@ -129,29 +116,6 @@ func ExtractHostID(m NodeMetadata) string { return hostid } -// IDAddresser tries to convert a node ID to a net.IP, if possible. -type IDAddresser func(string) net.IP - -// EndpointIDAddresser converts an endpoint node ID to an IP. -func EndpointIDAddresser(id string) net.IP { - fields := strings.SplitN(id, ScopeDelim, 3) - if len(fields) != 3 { - //log.Printf("EndpointIDAddresser: bad input %q", id) - return nil - } - return net.ParseIP(fields[1]) -} - -// AddressIDAddresser converts an address node ID to an IP. -func AddressIDAddresser(id string) net.IP { - fields := strings.SplitN(id, ScopeDelim, 2) - if len(fields) != 2 { - //log.Printf("AddressIDAddresser: bad input %q", id) - return nil - } - return net.ParseIP(fields[1]) -} - func isLoopback(address string) bool { ip := net.ParseIP(address) return ip != nil && ip.IsLoopback() diff --git a/report/id_list.go b/report/id_list.go index c77dc86deb..c69ed6e5f0 100644 --- a/report/id_list.go +++ b/report/id_list.go @@ -21,15 +21,6 @@ func MakeIDList(ids ...string) IDList { return IDList(ids) } -// Copy returns a copy of the IDList. -func (a IDList) Copy() IDList { - cp := make(IDList, len(a)) - for i, s := range a { - cp[i] = s - } - return cp -} - // Add is the only correct way to add ids to an IDList. func (a IDList) Add(ids ...string) IDList { for _, s := range ids { @@ -46,6 +37,13 @@ func (a IDList) Add(ids ...string) IDList { return a } +// Copy returns a copy of the IDList. +func (a IDList) Copy() IDList { + result := make(IDList, len(a)) + copy(result, a) + return result +} + // Merge all elements from a and b into a new list func (a IDList) Merge(b IDList) IDList { if len(b) == 0 { // Optimise special case, to avoid allocating diff --git a/report/id_test.go b/report/id_test.go index 6ec9158b6b..6dd1a84e86 100644 --- a/report/id_test.go +++ b/report/id_test.go @@ -1,8 +1,6 @@ package report_test import ( - "net" - "reflect" "testing" "github.com/weaveworks/scope/report" @@ -32,49 +30,6 @@ var ( unknownAddressNodeID = report.MakeAddressNodeID(unknownHostID, unknownAddress) ) -func TestAdjacencyID(t *testing.T) { - for _, bad := range []string{ - client54001EndpointNodeID, - client54002EndpointNodeID, - unknown1EndpointNodeID, - unknown2EndpointNodeID, - unknown3EndpointNodeID, - clientAddressNodeID, - serverAddressNodeID, - unknownAddressNodeID, - clientHostNodeID, - serverHostNodeID, - ";", - "", - } { - if srcNodeID, ok := report.ParseAdjacencyID(bad); ok { - t.Errorf("%q: expected failure, but got (%q)", bad, srcNodeID) - } - } - - for input, want := range map[string]struct{ srcNodeID string }{ - report.MakeAdjacencyID(report.MakeEndpointNodeID("a", "b", "c")): {report.MakeEndpointNodeID("a", "b", "c")}, - report.MakeAdjacencyID(report.MakeAddressNodeID("a", "b")): {report.MakeAddressNodeID("a", "b")}, - report.MakeAdjacencyID(report.MakeProcessNodeID("a", "b")): {report.MakeProcessNodeID("a", "b")}, - report.MakeAdjacencyID(report.MakeHostNodeID("a")): {report.MakeHostNodeID("a")}, - ">host.com;1.2.3.4": {"host.com;1.2.3.4"}, - ">a;b;c": {"a;b;c"}, - ">a;b": {"a;b"}, - ">a;": {"a;"}, - ">;b": {";b"}, - ">;": {";"}, - } { - srcNodeID, ok := report.ParseAdjacencyID(input) - if !ok { - t.Errorf("%q: not OK", input) - continue - } - if want, have := want.srcNodeID, srcNodeID; want != have { - t.Errorf("%q: want %q, have %q", input, want, have) - } - } -} - func TestEndpointNodeID(t *testing.T) { for _, bad := range []string{ clientAddressNodeID, @@ -158,31 +113,3 @@ func TestEdgeID(t *testing.T) { } } } - -func TestEndpointIDAddresser(t *testing.T) { - if nodeID := "1.2.4.5"; report.EndpointIDAddresser(nodeID) != nil { - t.Errorf("%q: bad node ID parsed as good", nodeID) - } - var ( - nodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "12345") - want = net.ParseIP(clientAddress) - have = report.EndpointIDAddresser(nodeID) - ) - if !reflect.DeepEqual(want, have) { - t.Errorf("want %s, have %s", want, have) - } -} - -func TestAddressIDAddresser(t *testing.T) { - if nodeID := "1.2.4.5"; report.AddressIDAddresser(nodeID) != nil { - t.Errorf("%q: bad node ID parsed as good", nodeID) - } - var ( - nodeID = report.MakeAddressNodeID(clientHostID, clientAddress) - want = net.ParseIP(clientAddress) - have = report.AddressIDAddresser(nodeID) - ) - if !reflect.DeepEqual(want, have) { - t.Errorf("want %s, have %s", want, have) - } -} diff --git a/report/merge_test.go b/report/merge_test.go index 11674bd7c3..e443f53efa 100644 --- a/report/merge_test.go +++ b/report/merge_test.go @@ -14,95 +14,6 @@ const ( Domain = "domain" ) -func TestMergeAdjacency(t *testing.T) { - for name, c := range map[string]struct { - a, b, want report.Adjacency - }{ - "Empty b": { - a: report.Adjacency{ - "hostA|:192.168.1.1:12345": report.MakeIDList(":192.168.1.2:80"), - "hostA|:192.168.1.1:8888": report.MakeIDList(":1.2.3.4:22"), - "hostB|:192.168.1.2:80": report.MakeIDList(":192.168.1.1:12345"), - }, - b: report.Adjacency{}, - want: report.Adjacency{ - "hostA|:192.168.1.1:12345": report.MakeIDList(":192.168.1.2:80"), - "hostA|:192.168.1.1:8888": report.MakeIDList(":1.2.3.4:22"), - "hostB|:192.168.1.2:80": report.MakeIDList(":192.168.1.1:12345"), - }, - }, - "Empty a": { - a: report.Adjacency{}, - b: report.Adjacency{ - "hostA|:192.168.1.1:12345": report.MakeIDList(":192.168.1.2:80"), - "hostA|:192.168.1.1:8888": report.MakeIDList(":1.2.3.4:22"), - "hostB|:192.168.1.2:80": report.MakeIDList(":192.168.1.1:12345"), - }, - want: report.Adjacency{ - "hostA|:192.168.1.1:12345": report.MakeIDList(":192.168.1.2:80"), - "hostA|:192.168.1.1:8888": report.MakeIDList(":1.2.3.4:22"), - "hostB|:192.168.1.2:80": report.MakeIDList(":192.168.1.1:12345"), - }, - }, - "Same address": { - a: report.Adjacency{ - "hostA|:192.168.1.1:12345": report.MakeIDList(":192.168.1.2:80"), - }, - b: report.Adjacency{ - "hostA|:192.168.1.1:12345": report.MakeIDList(":192.168.1.2:8080"), - }, - want: report.Adjacency{ - "hostA|:192.168.1.1:12345": report.MakeIDList( - ":192.168.1.2:80", ":192.168.1.2:8080", - ), - }, - }, - "No duplicates": { - a: report.Adjacency{ - "hostA|:192.168.1.1:12345": report.MakeIDList( - ":192.168.1.2:80", - ":192.168.1.2:8080", - ":192.168.1.2:555", - ), - }, - b: report.Adjacency{ - "hostA|:192.168.1.1:12345": report.MakeIDList( - ":192.168.1.2:8080", - ":192.168.1.2:80", - ":192.168.1.2:444", - ), - }, - want: report.Adjacency{ - "hostA|:192.168.1.1:12345": []string{ - ":192.168.1.2:444", - ":192.168.1.2:555", - ":192.168.1.2:80", - ":192.168.1.2:8080", - }, - }, - }, - "Double keys": { - a: report.Adjacency{ - "key1": report.MakeIDList("a", "c", "d", "b"), - "key2": report.MakeIDList("c", "a"), - }, - b: report.Adjacency{ - "key1": report.MakeIDList("a", "b", "e"), - "key3": report.MakeIDList("e", "a", "a", "a", "e"), - }, - want: report.Adjacency{ - "key1": report.MakeIDList("a", "b", "c", "d", "e"), - "key2": report.MakeIDList("a", "c"), - "key3": report.MakeIDList("a", "e"), - }, - }, - } { - if have := c.a.Merge(c.b); !reflect.DeepEqual(c.want, have) { - t.Errorf("%s: want\n\t%#v\nhave\n\t%#v", name, c.want, have) - } - } -} - func TestMergeEdgeMetadatas(t *testing.T) { for name, c := range map[string]struct { a, b, want report.EdgeMetadatas diff --git a/report/topology.go b/report/topology.go index 64efb55009..5202c5aa7f 100644 --- a/report/topology.go +++ b/report/topology.go @@ -6,10 +6,10 @@ import ( ) // Topology describes a specific view of a network. It consists of nodes and -// edges, represented by Adjacency, and metadata about those nodes and edges, -// represented by EdgeMetadatas and NodeMetadatas respectively. +// edges, and metadata about those nodes and edges, represented by EdgeMetadatas +// and NodeMetadatas respectively. Edges are directional, and embedded in the +// NodeMetadata. type Topology struct { - Adjacency EdgeMetadatas NodeMetadatas } @@ -17,7 +17,6 @@ type Topology struct { // MakeTopology gives you a Topology. func MakeTopology() Topology { return Topology{ - Adjacency: map[string]IDList{}, EdgeMetadatas: map[string]EdgeMetadata{}, NodeMetadatas: map[string]NodeMetadata{}, } @@ -26,7 +25,6 @@ func MakeTopology() Topology { // Copy returns a value copy of the Topology. func (t Topology) Copy() Topology { return Topology{ - Adjacency: t.Adjacency.Copy(), EdgeMetadatas: t.EdgeMetadatas.Copy(), NodeMetadatas: t.NodeMetadatas.Copy(), } @@ -36,35 +34,11 @@ func (t Topology) Copy() Topology { // The original is not modified. func (t Topology) Merge(other Topology) Topology { return Topology{ - Adjacency: t.Adjacency.Merge(other.Adjacency), EdgeMetadatas: t.EdgeMetadatas.Merge(other.EdgeMetadatas), NodeMetadatas: t.NodeMetadatas.Merge(other.NodeMetadatas), } } -// Adjacency is an adjacency-list encoding of the topology. Keys are adjacency -// IDs, values are lists of node IDs. -type Adjacency map[string]IDList - -// Copy returns a value copy of the adjacency. -func (a Adjacency) Copy() Adjacency { - cp := make(Adjacency, len(a)) - for k, v := range a { - cp[k] = v.Copy() - } - return cp -} - -// Merge merges the other object into this one, and returns the result object. -// The original is not modified. -func (a Adjacency) Merge(other Adjacency) Adjacency { - cp := a.Copy() - for k, v := range other { - cp[k] = cp[k].Merge(v) - } - return cp -} - // EdgeMetadatas collect metadata about each edge in a topology. Keys are a // concatenation of node IDs. type EdgeMetadatas map[string]EdgeMetadata @@ -173,8 +147,9 @@ func (e EdgeMetadata) Flatten(other EdgeMetadata) EdgeMetadata { // NodeMetadata describes a superset of the metadata that probes can collect // about a given node in a given topology. type NodeMetadata struct { - Metadata map[string]string - Counters map[string]int + Metadata map[string]string + Counters map[string]int + Adjacency IDList } // MakeNodeMetadata creates a new NodeMetadata with no initial metadata. @@ -185,8 +160,9 @@ func MakeNodeMetadata() NodeMetadata { // MakeNodeMetadataWith creates a new NodeMetadata with the supplied map. func MakeNodeMetadataWith(m map[string]string) NodeMetadata { return NodeMetadata{ - Metadata: m, - Counters: map[string]int{}, + Metadata: m, + Counters: map[string]int{}, + Adjacency: MakeIDList(), } } @@ -199,6 +175,7 @@ func (n NodeMetadata) Copy() NodeMetadata { for k, v := range n.Counters { cp.Counters[k] = v } + cp.Adjacency = n.Adjacency.Copy() return cp } @@ -213,6 +190,7 @@ func (n NodeMetadata) Merge(other NodeMetadata) NodeMetadata { for k, v := range other.Counters { cp.Counters[k] = n.Counters[k] + v } + cp.Adjacency = cp.Adjacency.Merge(other.Adjacency) return cp } @@ -227,48 +205,30 @@ 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 { - 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 { - errs = append(errs, fmt.Sprintf("adjacency entries missing for source node ID %q (from edge %q)", srcNodeID, edgeID)) - continue - } - if !dstNodeIDs.Contains(dstNodeID) { - errs = append(errs, fmt.Sprintf("adjacency destination missing for destination node ID %q (from edge %q)", dstNodeID, edgeID)) - } - } - - // Check all adjancency keys has entries in NodeMetadata. - for adjacencyID, dsts := range t.Adjacency { - srcNodeID, ok := ParseAdjacencyID(adjacencyID) - if !ok { - errs = append(errs, fmt.Sprintf("invalid adjacency ID %q", adjacencyID)) - continue - } - 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)) - } - } + // For each edge, ensure they are connected in the right direction + if src, ok := t.NodeMetadatas[srcNodeID]; !ok { + errs = append(errs, fmt.Sprintf("node %s metadatas missing for edge %q", srcNodeID, edgeID)) + } else if !src.Adjacency.Contains(dstNodeID) { + errs = append(errs, fmt.Sprintf("adjacency destination missing for destination node ID %q (from edge %q)", srcNodeID, edgeID)) } } // Check all node metadatas are valid, and the keys are parseable, i.e. // contain a scope. - for nodeID := range t.NodeMetadatas { - if t.NodeMetadatas[nodeID].Metadata == nil { + for nodeID, nmd := range t.NodeMetadatas { + if nmd.Metadata == nil { errs = append(errs, fmt.Sprintf("node ID %q has nil metadata", nodeID)) } if _, _, ok := ParseNodeID(nodeID); !ok { errs = append(errs, fmt.Sprintf("invalid node ID %q", nodeID)) } + + // Check all adjancency keys has entries in NodeMetadata. + for _, dstNodeID := range nmd.Adjacency { + if _, ok := t.NodeMetadatas[dstNodeID]; !ok { + errs = append(errs, fmt.Sprintf("node metadata missing from adjacency %q -> %q", nodeID, dstNodeID)) + } + } } if len(errs) > 0 { diff --git a/test/report_fixture.go b/test/report_fixture.go index aad44c11eb..99a9c88a6b 100644 --- a/test/report_fixture.go +++ b/test/report_fixture.go @@ -18,18 +18,22 @@ var ( ClientIP = "10.10.10.20" ServerIP = "192.168.1.1" - ClientPort54001 = "54001" - ClientPort54010 = "54010" - ClientPort54002 = "54002" - ClientPort54020 = "54020" - ClientPort12345 = "12345" - ServerPort = "80" UnknownClient1IP = "10.10.10.10" UnknownClient2IP = "10.10.10.10" UnknownClient3IP = "10.10.10.11" RandomClientIP = "51.52.53.54" GoogleIP = "8.8.8.8" + ClientPort54001 = "54001" + ClientPort54002 = "54002" + ServerPort = "80" + UnknownClient1Port = "54010" + UnknownClient2Port = "54020" + UnknownClient3Port = "54020" + RandomClientPort = "12345" + GooglePort = "80" + NonContainerClientPort = "46789" + ClientHostName = ClientHostID ServerHostName = ServerHostID @@ -46,15 +50,15 @@ var ( ClientHostNodeID = report.MakeHostNodeID(ClientHostID) ServerHostNodeID = report.MakeHostNodeID(ServerHostID) - Client54001NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54001) // curl (1) - Client54002NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54002) // curl (2) - Server80NodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, ServerPort) // apache - UnknownClient1NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient1IP, "54010") // we want to ensure two unknown clients, connnected - UnknownClient2NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient2IP, "54020") // to the same server, are deduped. - UnknownClient3NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient3IP, "54020") // Check this one isn't deduped - RandomClientNodeID = report.MakeEndpointNodeID(ServerHostID, RandomClientIP, "12345") // this should become an internet node - NonContainerNodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, "46789") - GoogleEndpointNodeID = report.MakeEndpointNodeID(ServerHostID, GoogleIP, "80") + Client54001NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54001) // curl (1) + Client54002NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54002) // curl (2) + Server80NodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, ServerPort) // apache + UnknownClient1NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient1IP, UnknownClient1Port) // we want to ensure two unknown clients, connnected + UnknownClient2NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient2IP, UnknownClient2Port) // to the same server, are deduped. + UnknownClient3NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient3IP, UnknownClient3Port) // Check this one isn't deduped + RandomClientNodeID = report.MakeEndpointNodeID(ServerHostID, RandomClientIP, RandomClientPort) // this should become an internet node + NonContainerNodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, NonContainerClientPort) + GoogleEndpointNodeID = report.MakeEndpointNodeID(ServerHostID, GoogleIP, GooglePort) ClientProcess1NodeID = report.MakeProcessNodeID(ClientHostID, Client1PID) ClientProcess2NodeID = report.MakeProcessNodeID(ClientHostID, Client2PID) @@ -73,51 +77,92 @@ var ( ClientContainerImageName = "image/client" ServerContainerImageName = "image/server" - ClientAddressNodeID = report.MakeAddressNodeID(ClientHostID, "10.10.10.20") - ServerAddressNodeID = report.MakeAddressNodeID(ServerHostID, "192.168.1.1") - UnknownAddress1NodeID = report.MakeAddressNodeID(ServerHostID, "10.10.10.10") - UnknownAddress2NodeID = report.MakeAddressNodeID(ServerHostID, "10.10.10.11") - RandomAddressNodeID = report.MakeAddressNodeID(ServerHostID, "51.52.53.54") // this should become an internet node + ClientAddressNodeID = report.MakeAddressNodeID(ClientHostID, ClientIP) + ServerAddressNodeID = report.MakeAddressNodeID(ServerHostID, ServerIP) + UnknownAddress1NodeID = report.MakeAddressNodeID(ServerHostID, UnknownClient1IP) + UnknownAddress2NodeID = report.MakeAddressNodeID(ServerHostID, UnknownClient2IP) + UnknownAddress3NodeID = report.MakeAddressNodeID(ServerHostID, UnknownClient3IP) + RandomAddressNodeID = report.MakeAddressNodeID(ServerHostID, RandomClientIP) // this should become an internet node Report = report.Report{ Endpoint: report.Topology{ - Adjacency: report.Adjacency{ - 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), - report.MakeAdjacencyID(NonContainerNodeID): report.MakeIDList(GoogleEndpointNodeID), - }, NodeMetadatas: report.NodeMetadatas{ // NodeMetadata is arbitrary. We're free to put only precisely what we // care to test into the fixture. Just be sure to include the bits // that the mapping funcs extract :) - Client54001NodeID: report.MakeNodeMetadataWith(map[string]string{ - endpoint.Addr: ClientIP, - endpoint.Port: ClientPort54001, - process.PID: Client1PID, - report.HostNodeID: ClientHostNodeID, - }), - Client54002NodeID: report.MakeNodeMetadataWith(map[string]string{ - endpoint.Addr: ClientIP, - endpoint.Port: ClientPort54002, - process.PID: Client2PID, - report.HostNodeID: ClientHostNodeID, - }), - Server80NodeID: report.MakeNodeMetadataWith(map[string]string{ - endpoint.Addr: ServerIP, - endpoint.Port: ServerPort, - process.PID: ServerPID, - report.HostNodeID: ServerHostNodeID, - }), - NonContainerNodeID: report.MakeNodeMetadataWith(map[string]string{ - endpoint.Addr: ServerIP, - endpoint.Port: "46789", - process.PID: NonContainerPID, - report.HostNodeID: ServerHostNodeID, - }), + Client54001NodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: ClientIP, + endpoint.Port: ClientPort54001, + process.PID: Client1PID, + report.HostNodeID: ClientHostNodeID, + }, + Adjacency: report.MakeIDList(Server80NodeID), + }, + Client54002NodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: ClientIP, + endpoint.Port: ClientPort54002, + process.PID: Client2PID, + report.HostNodeID: ClientHostNodeID, + }, + Adjacency: report.MakeIDList(Server80NodeID), + }, + Server80NodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: ServerIP, + endpoint.Port: ServerPort, + process.PID: ServerPID, + report.HostNodeID: ServerHostNodeID, + }, + Adjacency: report.MakeIDList(), + }, + NonContainerNodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: ServerIP, + endpoint.Port: NonContainerClientPort, + process.PID: NonContainerPID, + report.HostNodeID: ServerHostNodeID, + }, + Adjacency: report.MakeIDList(GoogleEndpointNodeID), + }, + + // Probe pseudo nodes + UnknownClient1NodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: UnknownClient1IP, + endpoint.Port: UnknownClient1Port, + }, + Adjacency: report.MakeIDList(Server80NodeID), + }, + UnknownClient2NodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: UnknownClient2IP, + endpoint.Port: UnknownClient2Port, + }, + Adjacency: report.MakeIDList(Server80NodeID), + }, + UnknownClient3NodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: UnknownClient3IP, + endpoint.Port: UnknownClient3Port, + }, + Adjacency: report.MakeIDList(Server80NodeID), + }, + RandomClientNodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: RandomClientIP, + endpoint.Port: RandomClientPort, + }, + Adjacency: report.MakeIDList(Server80NodeID), + }, + GoogleEndpointNodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: GoogleIP, + endpoint.Port: GooglePort, + }, + Adjacency: report.MakeIDList(), + }, }, EdgeMetadatas: report.EdgeMetadatas{ report.MakeEdgeID(Client54001NodeID, Server80NodeID): report.EdgeMetadata{ @@ -147,7 +192,6 @@ var ( }, }, Process: report.Topology{ - Adjacency: report.Adjacency{}, NodeMetadatas: report.NodeMetadatas{ ClientProcess1NodeID: report.MakeNodeMetadataWith(map[string]string{ process.PID: Client1PID, @@ -210,21 +254,43 @@ var ( }, }, Address: report.Topology{ - Adjacency: report.Adjacency{ - 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{ - endpoint.Addr: ClientIP, - report.HostNodeID: ClientHostNodeID, - }), + ClientAddressNodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: ClientIP, + report.HostNodeID: ClientHostNodeID, + }, + Adjacency: report.MakeIDList(ServerAddressNodeID), + }, ServerAddressNodeID: report.MakeNodeMetadataWith(map[string]string{ endpoint.Addr: ServerIP, report.HostNodeID: ServerHostNodeID, }), + + UnknownAddress1NodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: UnknownClient1IP, + }, + Adjacency: report.MakeIDList(ServerAddressNodeID), + }, + UnknownAddress2NodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: UnknownClient2IP, + }, + Adjacency: report.MakeIDList(ServerAddressNodeID), + }, + UnknownAddress3NodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: UnknownClient3IP, + }, + Adjacency: report.MakeIDList(ServerAddressNodeID), + }, + RandomAddressNodeID: report.NodeMetadata{ + Metadata: map[string]string{ + endpoint.Addr: RandomClientIP, + }, + Adjacency: report.MakeIDList(ServerAddressNodeID), + }, }, EdgeMetadatas: report.EdgeMetadatas{ report.MakeEdgeID(ClientAddressNodeID, ServerAddressNodeID): report.EdgeMetadata{ @@ -233,7 +299,6 @@ var ( }, }, Host: report.Topology{ - Adjacency: report.Adjacency{}, NodeMetadatas: report.NodeMetadatas{ ClientHostNodeID: report.MakeNodeMetadataWith(map[string]string{ "host_name": ClientHostName,