Improve test coverage; SquashRemote -> Squash

Also, remove some dead code.
This commit is contained in:
Peter Bourgon
2015-06-09 15:54:06 +02:00
parent d438261742
commit aa0a754363
10 changed files with 259 additions and 359 deletions

View File

@@ -133,5 +133,5 @@ func (s StaticReport) Report() report.Report {
},
},
}
return testReport.SquashRemote()
return testReport.Squash()
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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)
)

View File

@@ -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

View File

@@ -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)
)

View File

@@ -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
}

View File

@@ -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 "<src node ID>|<dst node ID>". 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
}

View File

@@ -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))
}
}

View File

@@ -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 {