diff --git a/app/mock_reporter_test.go b/app/mock_reporter_test.go index cdc4f9fda..ad7463677 100644 --- a/app/mock_reporter_test.go +++ b/app/mock_reporter_test.go @@ -133,5 +133,5 @@ func (s StaticReport) Report() report.Report { }, }, } - return testReport.SquashRemote() + return testReport.Squash() } diff --git a/app/report_lifo.go b/app/report_lifo.go index 398015a2f..94c594e74 100644 --- a/app/report_lifo.go +++ b/app/report_lifo.go @@ -41,7 +41,7 @@ func NewReportLIFO(r reporter, maxAge time.Duration) *ReportLIFO { select { case report := <-r.Reports(): // Incoming report from the collecter. - report = report.SquashRemote() // TODO?: make this a CLI argument. + report = report.Squash() // TODO?: make this a CLI argument. tr := timedReport{ Timestamp: time.Now(), Report: report, diff --git a/experimental/graphviz/report_lifo.go b/experimental/graphviz/report_lifo.go index 1f7ba6aa0..d28572e45 100644 --- a/experimental/graphviz/report_lifo.go +++ b/experimental/graphviz/report_lifo.go @@ -41,7 +41,7 @@ func NewReportLIFO(r reporter, maxAge time.Duration) *ReportLIFO { for { select { case report := <-r.Reports(): - report = report.SquashRemote() + report = report.Squash() tr := timedReport{ Timestamp: time.Now(), Report: report, diff --git a/report/fixture_test.go b/report/fixture_test.go deleted file mode 100644 index 9aa92c23f..000000000 --- a/report/fixture_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package report_test - -import ( - "github.com/weaveworks/scope/report" -) - -var ( - clientHostID = "client.host.com" - clientHostName = clientHostID - clientHostNodeID = report.MakeHostNodeID(clientHostID) - clientAddress = "10.10.10.20" - serverHostID = "server.host.com" - serverHostName = serverHostID - serverHostNodeID = report.MakeHostNodeID(serverHostID) - serverAddress = "10.10.10.1" - unknownHostID = "" // by definition, we don't know it - unknownAddress = "172.16.93.112" // will be a pseudonode, no corresponding host - - client54001EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54001") // i.e. curl - client54002EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54002") // also curl - server80EndpointNodeID = report.MakeEndpointNodeID(serverHostID, serverAddress, "80") // i.e. apache - unknown1EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10001") - unknown2EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10002") - unknown3EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10003") - - clientAddressNodeID = report.MakeAddressNodeID(clientHostID, clientAddress) - serverAddressNodeID = report.MakeAddressNodeID(serverHostID, serverAddress) - unknownAddressNodeID = report.MakeAddressNodeID(unknownHostID, unknownAddress) -) diff --git a/report/report.go b/report/report.go index 26f357dd7..d494f3682 100644 --- a/report/report.go +++ b/report/report.go @@ -74,16 +74,15 @@ func MakeReport() Report { } } -// SquashRemote folds all remote nodes into a special supernode. It uses the -// LocalNets of the hosts in HostMetadata to determine which addresses are -// local. -func (r Report) SquashRemote() Report { +// Squash squashes all non-local nodes in the report to a super-node called +// the Internet. +func (r Report) Squash() Report { localNetworks := r.LocalNetworks() - return Report{ - Endpoint: Squash(r.Endpoint, EndpointIDAddresser, localNetworks), - Address: Squash(r.Address, AddressIDAddresser, localNetworks), - Host: Squash(r.Host, PanicIDAddresser, localNetworks), - } + r.Endpoint = r.Endpoint.Squash(EndpointIDAddresser, localNetworks) + r.Address = r.Address.Squash(AddressIDAddresser, localNetworks) + //r.Process = r.Process.Squash(PanicIDAddresser, localNetworks) + r.Host = r.Host.Squash(PanicIDAddresser, localNetworks) + return r } // LocalNetworks returns a superset of the networks (think: CIDRs) that are diff --git a/report/report_fixture_test.go b/report/report_fixture_test.go new file mode 100644 index 000000000..010f3651b --- /dev/null +++ b/report/report_fixture_test.go @@ -0,0 +1,166 @@ +package report_test + +import ( + "github.com/weaveworks/scope/report" +) + +var reportFixture = report.Report{ + Endpoint: report.Topology{ + Adjacency: report.Adjacency{ + report.MakeAdjacencyID(clientHostID, client54001EndpointNodeID): report.MakeIDList(server80EndpointNodeID), + report.MakeAdjacencyID(clientHostID, client54002EndpointNodeID): report.MakeIDList(server80EndpointNodeID), + report.MakeAdjacencyID(serverHostID, server80EndpointNodeID): report.MakeIDList(client54001EndpointNodeID, client54002EndpointNodeID, unknown1EndpointNodeID, unknown2EndpointNodeID, unknown3EndpointNodeID), + }, + NodeMetadatas: report.NodeMetadatas{ + client54001EndpointNodeID: report.NodeMetadata{ + "process_node_id": report.MakeProcessNodeID(clientHostID, "4242"), + "address_node_id": report.MakeAddressNodeID(clientHostID, clientAddress), + }, + client54002EndpointNodeID: report.NodeMetadata{ + //"process_node_id": report.MakeProcessNodeID(clientHostID, "4242"), // leave it out, to test a branch in Render + "address_node_id": report.MakeAddressNodeID(clientHostID, clientAddress), + }, + server80EndpointNodeID: report.NodeMetadata{ + "process_node_id": report.MakeProcessNodeID(serverHostID, "215"), + "address_node_id": report.MakeAddressNodeID(serverHostID, serverAddress), + }, + + "process-not-available": report.NodeMetadata{}, // for TestProcess{PID,Name,Container[Name]} + "process-badly-linked": report.NodeMetadata{"process_node_id": "none"}, // for TestProcess{PID,Name,Container[Name]} + "process-no-container": report.NodeMetadata{"process_node_id": "no-container"}, // for TestProcessContainer[Name] + "address-not-available": report.NodeMetadata{}, // for TestAddressHostname + "address-badly-linked": report.NodeMetadata{"address_node_id": "none"}, // for TestAddressHostname + }, + EdgeMetadatas: report.EdgeMetadatas{ + report.MakeEdgeID(client54001EndpointNodeID, server80EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 10, // src -> dst + BytesIngress: 100, // src <- dst + }, + report.MakeEdgeID(client54002EndpointNodeID, server80EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 20, + BytesIngress: 200, + }, + report.MakeEdgeID(server80EndpointNodeID, client54001EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 100, + BytesIngress: 10, + }, + report.MakeEdgeID(server80EndpointNodeID, client54002EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 200, + BytesIngress: 20, + }, + report.MakeEdgeID(server80EndpointNodeID, unknown1EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 400, + BytesIngress: 40, + }, + report.MakeEdgeID(server80EndpointNodeID, unknown2EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 800, + BytesIngress: 80, + }, + report.MakeEdgeID(server80EndpointNodeID, unknown3EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 1600, + BytesIngress: 160, + }, + }, + }, + Address: report.Topology{ + Adjacency: report.Adjacency{ + report.MakeAdjacencyID(clientHostID, clientAddressNodeID): report.MakeIDList(serverAddressNodeID), + report.MakeAdjacencyID(serverHostID, serverAddressNodeID): report.MakeIDList(clientAddressNodeID, unknownAddressNodeID), + }, + NodeMetadatas: report.NodeMetadatas{ + clientAddressNodeID: report.NodeMetadata{ + "host_name": "client.host.com", + }, + serverAddressNodeID: report.NodeMetadata{}, + + "no-host-name": report.NodeMetadata{}, + }, + EdgeMetadatas: report.EdgeMetadatas{ + report.MakeEdgeID(clientAddressNodeID, serverAddressNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 10 + 20 + 1, + BytesIngress: 100 + 200 + 2, + }, + report.MakeEdgeID(serverAddressNodeID, clientAddressNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 100 + 200 + 3, + BytesIngress: 10 + 20 + 4, + }, + report.MakeEdgeID(serverAddressNodeID, unknownAddressNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 400 + 800 + 1600 + 5, + BytesIngress: 40 + 80 + 160 + 6, + }, + }, + }, + //Process: report.Topology{ + // Adjacency: report.Adjacency{}, + // NodeMetadatas: report.NodeMetadatas{ + // report.MakeProcessNodeID(clientHostID, "4242"): report.NodeMetadata{ + // "host_name": "client.host.com", + // "pid": "4242", + // "process_name": "curl", + // "docker_container_id": "a1b2c3d4e5", + // "docker_container_name": "fixture-container", + // "docker_image_id": "0000000000", + // "docker_image_name": "fixture/container:latest", + // }, + // report.MakeProcessNodeID(serverHostID, "215"): report.NodeMetadata{ + // "pid": "215", + // "process_name": "apache", + // }, + // + // "no-container": report.NodeMetadata{}, + // }, + // EdgeMetadatas: report.EdgeMetadatas{}, + //}, + Host: report.Topology{ + Adjacency: report.Adjacency{}, + NodeMetadatas: report.NodeMetadatas{ + report.MakeHostNodeID(clientHostID): report.NodeMetadata{ + "host_name": clientHostName, + "local_networks": "10.10.10.0/24", + "os": "OS/2", + "load": "0.11 0.22 0.33", + }, + report.MakeHostNodeID(serverHostID): report.NodeMetadata{ + "host_name": serverHostName, + "local_networks": "10.10.10.0/24", + "os": "Linux", + "load": "0.01 0.01 0.01", + }, + }, + EdgeMetadatas: report.EdgeMetadatas{}, + }, +} + +var ( + clientHostID = "client.host.com" + clientHostName = clientHostID + clientHostNodeID = report.MakeHostNodeID(clientHostID) + clientAddress = "10.10.10.20" + serverHostID = "server.host.com" + serverHostName = serverHostID + serverHostNodeID = report.MakeHostNodeID(serverHostID) + serverAddress = "10.10.10.1" + unknownHostID = "" // by definition, we don't know it + unknownAddress = "172.16.93.112" // will be a pseudonode, no corresponding host + + client54001EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54001") // i.e. curl + client54002EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54002") // also curl + server80EndpointNodeID = report.MakeEndpointNodeID(serverHostID, serverAddress, "80") // i.e. apache + unknown1EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10001") + unknown2EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10002") + unknown3EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10003") + + clientAddressNodeID = report.MakeAddressNodeID(clientHostID, clientAddress) + serverAddressNodeID = report.MakeAddressNodeID(serverHostID, serverAddress) + unknownAddressNodeID = report.MakeAddressNodeID(unknownHostID, unknownAddress) +) diff --git a/report/report_test.go b/report/report_test.go index 80c499fb2..a2b5ce4be 100644 --- a/report/report_test.go +++ b/report/report_test.go @@ -1 +1,55 @@ -package report +package report_test + +import ( + "net" + "reflect" + "testing" + + "github.com/weaveworks/scope/report" +) + +func TestReportLocalNetworks(t *testing.T) { + r := report.MakeReport() + r.Merge(report.Report{Host: report.Topology{NodeMetadatas: report.NodeMetadatas{ + "nonets": {}, + "foo": {"local_networks": "10.0.0.1/8 192.168.1.1/24 10.0.0.1/8 badnet/33"}, + }}}) + if want, have := []*net.IPNet{ + mustParseCIDR("10.0.0.1/8"), + mustParseCIDR("192.168.1.1/24"), + }, r.LocalNetworks(); !reflect.DeepEqual(want, have) { + t.Errorf("want %+v, have %+v", want, have) + } +} + +func TestReportSquash(t *testing.T) { + { + want := report.Adjacency{ + report.MakeAdjacencyID(clientHostID, client54001EndpointNodeID): report.MakeIDList(server80EndpointNodeID), + report.MakeAdjacencyID(clientHostID, client54002EndpointNodeID): report.MakeIDList(server80EndpointNodeID), + report.MakeAdjacencyID(serverHostID, server80EndpointNodeID): report.MakeIDList(client54001EndpointNodeID, client54002EndpointNodeID, report.TheInternet), + } + have := reportFixture.Squash().Endpoint.Adjacency + if !reflect.DeepEqual(want, have) { + t.Error(diff(want, have)) + } + } + { + want := report.Adjacency{ + report.MakeAdjacencyID(clientHostID, clientAddressNodeID): report.MakeIDList(serverAddressNodeID), + report.MakeAdjacencyID(serverHostID, serverAddressNodeID): report.MakeIDList(clientAddressNodeID, report.TheInternet), + } + have := reportFixture.Squash().Address.Adjacency + if !reflect.DeepEqual(want, have) { + t.Error(diff(want, have)) + } + } +} + +func mustParseCIDR(s string) *net.IPNet { + _, ipNet, err := net.ParseCIDR(s) + if err != nil { + panic(err) + } + return ipNet +} diff --git a/report/squash.go b/report/squash.go deleted file mode 100644 index a25158d45..000000000 --- a/report/squash.go +++ /dev/null @@ -1,57 +0,0 @@ -package report - -import ( - "log" - "net" -) - -// Squash takes a Topology, and folds all remote nodes into a supernode. -func Squash(t Topology, f IDAddresser, localNets []*net.IPNet) Topology { - newTopo := NewTopology() - isRemote := func(ip net.IP) bool { return !netsContain(localNets, ip) } - - // If any node ID on the right-hand (destination) side of an adjacency - // list is remote, rename it to TheInternet. (We'll never have remote - // nodes on the left-hand (source) side of an adjacency list, by - // definition.) - for nodeID, adjacent := range t.Adjacency { - var newAdjacency IDList - for _, adjacentID := range adjacent { - if isRemote(f(adjacentID)) { - adjacentID = TheInternet - } - newAdjacency = newAdjacency.Add(adjacentID) - } - newTopo.Adjacency[nodeID] = newAdjacency - } - - // Edge metadata keys are "|". If the dst node - // ID is remote, rename it to TheInternet. - for key, metadata := range t.EdgeMetadatas { - srcNodeID, dstNodeID, ok := ParseEdgeID(key) - if !ok { - log.Printf("bad edge ID %q", key) - continue - } - if ip := f(dstNodeID); ip != nil && isRemote(ip) { - key = MakeEdgeID(srcNodeID, TheInternet) - } - - // Could be we're merging two keys into one now. - summedMetadata := newTopo.EdgeMetadatas[key] - summedMetadata.Flatten(metadata) - newTopo.EdgeMetadatas[key] = summedMetadata - } - - newTopo.NodeMetadatas = t.NodeMetadatas - return newTopo -} - -func netsContain(nets []*net.IPNet, ip net.IP) bool { - for _, net := range nets { - if net.Contains(ip) { - return true - } - } - return false -} diff --git a/report/squash_test.go b/report/squash_test.go deleted file mode 100644 index e8af6d8ee..000000000 --- a/report/squash_test.go +++ /dev/null @@ -1,260 +0,0 @@ -package report - -import ( - "net" - "reflect" - "testing" -) - -var ( - _, netdot1, _ = net.ParseCIDR("192.168.1.0/24") - _, netdot2, _ = net.ParseCIDR("192.168.2.0/24") -) - -func reportToSquash() Report { - return Report{ - Endpoint: Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1;12345": []string{";192.168.1.2;80"}, - "hostA|;192.168.1.1;8888": []string{";1.2.3.4;22", ";1.2.3.4;23"}, - "hostB|;192.168.1.2;80": []string{";192.168.1.1;12345"}, - "hostZ|;192.168.2.2;80": []string{";192.168.1.1;12345"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1;12345|;192.168.1.2;80": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1;8888|;1.2.3.4;22": EdgeMetadata{ - WithBytes: true, - BytesEgress: 200, - BytesIngress: 0, - }, - ";192.168.1.1;8888|;1.2.3.4;23": EdgeMetadata{ - WithBytes: true, - BytesEgress: 200, - BytesIngress: 0, - }, - ";192.168.1.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: NodeMetadatas{ - ";192.168.1.1;12345": NodeMetadata{ - "pid": "23128", - "name": "curl", - "domain": "node-a.local", - }, - ";192.168.1.1;8888": NodeMetadata{ - "pid": "55100", - "name": "ssh", - "domain": "node-a.local", - }, - ";192.168.1.2;80": NodeMetadata{ - "pid": "215", - "name": "apache", - "domain": "node-b.local", - }, - ";192.168.2.2;80": NodeMetadata{ - "pid": "213", - "name": "apache", - "domain": "node-z.local", - }, - }, - }, - - Address: Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1": []string{";192.168.1.2", ";1.2.3.4"}, - "hostB|;192.168.1.2": []string{";192.168.1.1"}, - "hostZ|;192.168.2.2": []string{";192.168.1.1"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1|;192.168.1.2": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1|;1.2.3.4": EdgeMetadata{ - WithBytes: true, - BytesEgress: 200, - BytesIngress: 0, - }, - ";192.168.1.2|;192.168.1.1": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2|;192.168.1.1": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: NodeMetadatas{ - ";192.168.1.1": NodeMetadata{ - "name": "host-a", - }, - ";192.168.1.2": NodeMetadata{ - "name": "host-b", - }, - ";192.168.2.2": NodeMetadata{ - "name": "host-z", - }, - }, - }, - - Host: Topology{ - Adjacency: Adjacency{}, - EdgeMetadatas: EdgeMetadatas{}, - NodeMetadatas: NodeMetadatas{ - MakeHostNodeID("hostA"): NodeMetadata{ - "host_name": "node-a.local", - "os": "Linux", - "local_networks": netdot1.String(), - }, - MakeHostNodeID("hostB"): NodeMetadata{ - "host_name": "node-b.local", - "os": "Linux", - "local_networks": netdot1.String(), - }, - MakeHostNodeID("hostZ"): NodeMetadata{ - "host_name": "node-z.local", - "os": "Linux", - "local_networks": netdot2.String(), - }, - }, - }, - } -} - -func TestSquashTopology(t *testing.T) { - // Tests just a topology - want := Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1;12345": []string{";192.168.1.2;80"}, - "hostA|;192.168.1.1;8888": []string{"theinternet"}, - "hostB|;192.168.1.2;80": []string{";192.168.1.1;12345"}, - "hostZ|;192.168.2.2;80": []string{";192.168.1.1;12345"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1;12345|;192.168.1.2;80": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1;8888|theinternet": EdgeMetadata{ - WithBytes: true, - BytesEgress: 2 * 200, - BytesIngress: 2 * 0, - }, - ";192.168.1.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: reportToSquash().Endpoint.NodeMetadatas, - } - - have := Squash(reportToSquash().Endpoint, EndpointIDAddresser, reportToSquash().LocalNetworks()) - if !reflect.DeepEqual(want, have) { - t.Errorf("want\n\t%#v, have\n\t%#v", want, have) - } -} - -func TestSquashReport(t *testing.T) { - // Tests a full report squash. - want := Report{ - Endpoint: Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1;12345": []string{";192.168.1.2;80"}, - "hostA|;192.168.1.1;8888": []string{"theinternet"}, - "hostB|;192.168.1.2;80": []string{";192.168.1.1;12345"}, - "hostZ|;192.168.2.2;80": []string{";192.168.1.1;12345"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1;12345|;192.168.1.2;80": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1;8888|theinternet": EdgeMetadata{ - WithBytes: true, - BytesEgress: 2 * 200, - BytesIngress: 2 * 0, - }, - ";192.168.1.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: reportToSquash().Endpoint.NodeMetadatas, - }, - Address: Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1": []string{";192.168.1.2", "theinternet"}, - "hostB|;192.168.1.2": []string{";192.168.1.1"}, - "hostZ|;192.168.2.2": []string{";192.168.1.1"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1|;192.168.1.2": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1|theinternet": EdgeMetadata{ - WithBytes: true, - BytesEgress: 200, - BytesIngress: 0, - }, - ";192.168.1.2|;192.168.1.1": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2|;192.168.1.1": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: NodeMetadatas{ - ";192.168.1.1": NodeMetadata{ - "name": "host-a", - }, - ";192.168.1.2": NodeMetadata{ - "name": "host-b", - }, - ";192.168.2.2": NodeMetadata{ - "name": "host-z", - }, - }, - }, - Host: reportToSquash().Host, - } - - have := reportToSquash().SquashRemote() - if !reflect.DeepEqual(want, have) { - t.Error(diff(want, have)) - } -} diff --git a/report/topology.go b/report/topology.go index 50affeb0a..1ad2c7393 100644 --- a/report/topology.go +++ b/report/topology.go @@ -2,6 +2,7 @@ package report import ( "log" + "net" "reflect" ) @@ -182,6 +183,32 @@ func (t Topology) EdgeMetadata(mapFunc MapFunc, srcRenderableID, dstRenderableID return metadata } +// Squash squashes all non-local nodes in the topology to a super-node called +// the Internet. +func (t Topology) Squash(f IDAddresser, localNets []*net.IPNet) Topology { + isRemote := func(ip net.IP) bool { return !netsContain(localNets, ip) } + for srcID, dstIDs := range t.Adjacency { + newDstIDs := make(IDList, 0, len(dstIDs)) + for _, dstID := range dstIDs { + if ip := f(dstID); ip != nil && isRemote(ip) { + dstID = TheInternet + } + newDstIDs = newDstIDs.Add(dstID) + } + t.Adjacency[srcID] = newDstIDs + } + return t +} + +func netsContain(nets []*net.IPNet, ip net.IP) bool { + for _, net := range nets { + if net.Contains(ip) { + return true + } + } + return false +} + // Diff is returned by TopoDiff. It represents the changes between two // RenderableNode maps. type Diff struct {