func TestReportLocalNetworks(t *testing.T) { r := report.MakeReport().Merge(report.Report{ Host: report.Topology{ Nodes: report.Nodes{ "nonets": report.MakeNode("nonets"), "foo": report.MakeNode("foo").WithSets(report.EmptySets. Add(host.LocalNetworks, report.MakeStringSet( "10.0.0.1/8", "192.168.1.1/24", "10.0.0.1/8", "badnet/33")), ), }, }, Overlay: report.Topology{ Nodes: report.Nodes{ "router": report.MakeNode("router").WithSets(report.EmptySets. Add(host.LocalNetworks, report.MakeStringSet("10.32.0.1/12")), ), }, }, }) want := report.Networks([]*net.IPNet{ mustParseCIDR("10.0.0.1/8"), mustParseCIDR("192.168.1.1/24"), mustParseCIDR("10.32.0.1/12"), }) have := render.LocalNetworks(r) if !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) } }
func TestMerge(t *testing.T) { var ( hostID = "xyz" src = newMockSource([]byte{}, nil) on = time.Millisecond off = time.Millisecond rpt = report.MakeReport() p = sniff.Packet{ SrcIP: "1.0.0.0", SrcPort: "1000", DstIP: "2.0.0.0", DstPort: "2000", Network: 512, Transport: 256, } _, ipnet, _ = net.ParseCIDR(p.SrcIP + "/24") // ;) localNets = report.Networks([]*net.IPNet{ipnet}) ) sniff.New(hostID, localNets, src, on, off).Merge(p, &rpt) var ( srcEndpointNodeID = report.MakeEndpointNodeID(hostID, p.SrcIP, p.SrcPort) dstEndpointNodeID = report.MakeEndpointNodeID(hostID, p.DstIP, p.DstPort) ) if want, have := (report.Topology{ Nodes: report.Nodes{ srcEndpointNodeID: report.MakeNode(srcEndpointNodeID).WithEdge(dstEndpointNodeID, report.EdgeMetadata{ EgressPacketCount: newu64(1), EgressByteCount: newu64(256), }), dstEndpointNodeID: report.MakeNode(dstEndpointNodeID), }, }), rpt.Endpoint; !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) } var ( srcAddressNodeID = report.MakeAddressNodeID(hostID, p.SrcIP) dstAddressNodeID = report.MakeAddressNodeID(hostID, p.DstIP) ) if want, have := (report.Topology{ Nodes: report.Nodes{ srcAddressNodeID: report.MakeNode(srcAddressNodeID).WithEdge(dstAddressNodeID, report.EdgeMetadata{ EgressPacketCount: newu64(1), EgressByteCount: newu64(512), }), dstAddressNodeID: report.MakeNode(dstAddressNodeID), }, }), rpt.Address; !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) } }
func TestNodeSetDelete(t *testing.T) { for _, testcase := range []struct { input report.NodeSet nodes []string want report.NodeSet }{ { input: report.NodeSet{}, nodes: []string{}, want: report.NodeSet{}, }, { input: report.EmptyNodeSet, nodes: []string{}, want: report.EmptyNodeSet, }, { input: report.MakeNodeSet(report.MakeNode("a")), nodes: []string{}, want: report.MakeNodeSet(report.MakeNode("a")), }, { input: report.EmptyNodeSet, nodes: []string{"a"}, want: report.EmptyNodeSet, }, { input: report.MakeNodeSet(report.MakeNode("a")), nodes: []string{"a"}, want: report.EmptyNodeSet, }, { input: report.MakeNodeSet(report.MakeNode("b")), nodes: []string{"a", "b"}, want: report.EmptyNodeSet, }, { input: report.MakeNodeSet(report.MakeNode("a")), nodes: []string{"c", "b"}, want: report.MakeNodeSet(report.MakeNode("a")), }, { input: report.MakeNodeSet(report.MakeNode("a"), report.MakeNode("c")), nodes: []string{"a", "a", "a"}, want: report.MakeNodeSet(report.MakeNode("c")), }, } { originalLen := testcase.input.Size() if want, have := testcase.want, testcase.input.Delete(testcase.nodes...); !reflect.DeepEqual(want, have) { t.Errorf("%v + %v: want %v, have %v", testcase.input, testcase.nodes, want, have) } if testcase.input.Size() != originalLen { t.Errorf("%v + %v: modified the original input!", testcase.input, testcase.nodes) } } }
func TestReduceRender(t *testing.T) { renderer := render.Reduce([]render.Renderer{ mockRenderer{Nodes: report.Nodes{"foo": report.MakeNode("foo")}}, mockRenderer{Nodes: report.Nodes{"bar": report.MakeNode("bar")}}, }) want := report.Nodes{ "foo": report.MakeNode("foo"), "bar": report.MakeNode("bar"), } have := renderer.Render(report.MakeReport(), render.FilterNoop) if !reflect.DeepEqual(want, have) { t.Errorf("want %+v, have %+v", want, have) } }
func TestFilterRender(t *testing.T) { renderer := mockRenderer{Nodes: report.Nodes{ "foo": report.MakeNode("foo").WithAdjacent("bar"), "bar": report.MakeNode("bar").WithAdjacent("foo"), "baz": report.MakeNode("baz"), }} have := report.MakeIDList() for id := range renderer.Render(report.MakeReport(), render.FilterUnconnected) { have = have.Add(id) } want := report.MakeIDList("foo", "bar") if !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } }
func TestApply(t *testing.T) { var ( endpointNodeID = "c" endpointNode = report.MakeNodeWith(endpointNodeID, map[string]string{"5": "6"}) ) p := New(0, 0, nil) p.AddTagger(NewTopologyTagger()) r := report.MakeReport() r.Endpoint.AddNode(endpointNode) r = p.tag(r) for _, tuple := range []struct { want report.Node from report.Topology via string }{ {endpointNode.Merge(report.MakeNode("c").WithTopology(report.Endpoint)), r.Endpoint, endpointNodeID}, } { if want, have := tuple.want, tuple.from.Nodes[tuple.via]; !reflect.DeepEqual(want, have) { t.Errorf("want %+v, have %+v", want, have) } } }
func TestMakeNodeSet(t *testing.T) { for _, testcase := range []struct { inputs []string wants []string }{ {inputs: nil, wants: nil}, { inputs: []string{"a"}, wants: []string{"a"}, }, { inputs: []string{"b", "c", "a"}, wants: []string{"a", "b", "c"}, }, { inputs: []string{"a", "a", "a"}, wants: []string{"a"}, }, } { var inputs []report.Node for _, id := range testcase.inputs { inputs = append(inputs, report.MakeNode(id)) } set := report.MakeNodeSet(inputs...) var have []string set.ForEach(func(node report.Node) { have = append(have, node.ID) }) if !reflect.DeepEqual(testcase.wants, have) { t.Errorf("%#v: want %#v, have %#v", testcase.inputs, testcase.wants, have) } } }
func benchmarkMerger(b *testing.B, merger app.Merger) { makeReport := func() report.Report { rpt := report.MakeReport() for i := 0; i < 100; i++ { rpt.Endpoint.AddNode(report.MakeNode(fmt.Sprintf("%x", rand.Int63()))) } return rpt } reports := []report.Report{} for i := 0; i < numHosts*5; i++ { reports = append(reports, makeReport()) } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { // replace 1/3 of hosts work of reports & merge them all for i := 0; i < numHosts/3; i++ { reports[rand.Intn(len(reports))] = makeReport() } merger.Merge(reports) } }
// Report implements Reporter. func (w *Weave) Report() (report.Report, error) { w.mtx.RLock() defer w.mtx.RUnlock() r := report.MakeReport() r.Container = r.Container.WithMetadataTemplates(report.MetadataTemplates{ WeaveMACAddress: {ID: WeaveMACAddress, Label: "Weave MAC", From: report.FromLatest, Priority: 17}, WeaveDNSHostname: {ID: WeaveDNSHostname, Label: "Weave DNS Name", From: report.FromLatest, Priority: 18}, }) for _, peer := range w.statusCache.Router.Peers { r.Overlay.AddNode(report.MakeNodeWith(report.MakeOverlayNodeID(peer.Name), map[string]string{ WeavePeerName: peer.Name, WeavePeerNickName: peer.NickName, })) } if w.statusCache.IPAM.DefaultSubnet != "" { r.Overlay.AddNode( report.MakeNode(report.MakeOverlayNodeID(w.statusCache.Router.Name)).WithSets( report.MakeSets().Add(host.LocalNetworks, report.MakeStringSet(w.statusCache.IPAM.DefaultSubnet)), ), ) } return r, nil }
func TestMapProcess2Container(t *testing.T) { for _, input := range []testcase{ {"empty", report.MakeNode("empty"), true}, {"basic process", report.MakeNodeWith("basic", map[string]string{process.PID: "201", docker.ContainerID: "a1b2c3"}), true}, {"uncontained", report.MakeNodeWith("uncontained", map[string]string{process.PID: "201", report.HostNodeID: report.MakeHostNodeID("foo")}), true}, } { testMap(t, render.MapProcess2Container, input) } }
// FIXME: Hideous hack to remove persistent-connection edges to virtual service // IPs attributed to the internet. We add each service IP as a /32 network // (the global service-cluster-ip-range is not exposed by the API // server so we treat each IP as a /32 network see // https://github.com/kubernetes/kubernetes/issues/25533). // The right way of fixing this is performing DNAT mapping on persistent // connections for which we don't have a robust solution // (see https://$GITHUB_URI/issues/1491) func (r *Reporter) hostTopology(services []Service) report.Topology { localNetworks := report.EmptyStringSet for _, service := range services { localNetworks = localNetworks.Add(service.ClusterIP() + "/32") } node := report.MakeNode(report.MakeHostNodeID(r.hostID)) node = node.WithSets(report.EmptySets. Add(host.LocalNetworks, localNetworks)) return report.MakeTopology().AddNode(node) }
// PruneNode returns a copy of the Node with all information not strictly // necessary for rendering nodes and edges stripped away. Specifically, that // means cutting out parts of the Node. func PruneNode(node report.Node) report.Node { prunedChildren := report.MakeNodeSet() node.Children.ForEach(func(child report.Node) { prunedChildren = prunedChildren.Add(PruneNode(child)) }) return report.MakeNode( node.ID). WithTopology(node.Topology). WithAdjacent(node.Adjacency.Copy()...). WithChildren(prunedChildren) }
func TestMapRender3(t *testing.T) { // 3. Check we can remap adjacencies mapper := render.Map{ MapFunc: func(nodes report.Node, _ report.Networks) report.Nodes { id := "_" + nodes.ID return report.Nodes{id: report.MakeNode(id)} }, Renderer: mockRenderer{Nodes: report.Nodes{ "foo": report.MakeNode("foo").WithAdjacent("baz"), "baz": report.MakeNode("baz").WithAdjacent("foo"), }}, } want := report.Nodes{ "_foo": report.MakeNode("_foo").WithAdjacent("_baz"), "_baz": report.MakeNode("_baz").WithAdjacent("_foo"), } have := mapper.Render(report.MakeReport(), render.FilterNoop) if !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } }
func TestFilterRender2(t *testing.T) { // Test adjacencies are removed for filtered nodes. filter := func(renderer render.Renderer) render.Renderer { return &render.Filter{ FilterFunc: func(node report.Node) bool { return node.ID != "bar" }, Renderer: renderer, } } renderer := mockRenderer{Nodes: report.Nodes{ "foo": report.MakeNode("foo").WithAdjacent("bar"), "bar": report.MakeNode("bar").WithAdjacent("foo"), "baz": report.MakeNode("baz"), }} have := renderer.Render(report.MakeReport(), filter) if have["foo"].Adjacency.Contains("bar") { t.Error("adjacencies for removed nodes should have been removed") } }
func TestMapRender2(t *testing.T) { // 2. Check we can remap two nodes into one mapper := render.Map{ MapFunc: func(nodes report.Node, _ report.Networks) report.Nodes { return report.Nodes{ "bar": report.MakeNode("bar"), } }, Renderer: mockRenderer{Nodes: report.Nodes{ "foo": report.MakeNode("foo"), "baz": report.MakeNode("baz"), }}, } want := report.Nodes{ "bar": report.MakeNode("bar"), } have := mapper.Render(report.MakeReport(), render.FilterNoop) if !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } }
func TestCollector(t *testing.T) { ctx := context.Background() window := 10 * time.Second c := app.NewCollector(window) r1 := report.MakeReport() r1.Endpoint.AddNode(report.MakeNode("foo")) r2 := report.MakeReport() r2.Endpoint.AddNode(report.MakeNode("foo")) have, err := c.Report(ctx) if err != nil { t.Error(err) } if want := report.MakeReport(); !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } c.Add(ctx, r1) have, err = c.Report(ctx) if err != nil { t.Error(err) } if want := r1; !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } c.Add(ctx, r2) merged := report.MakeReport() merged = merged.Merge(r1) merged = merged.Merge(r2) have, err = c.Report(ctx) if err != nil { t.Error(err) } if want := merged; !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } }
func TestMerger(t *testing.T) { // Use 3 reports to check the pair-wise merging in SmartMerger report1 := report.MakeReport() report1.Endpoint.AddNode(report.MakeNode("foo")) report2 := report.MakeReport() report2.Endpoint.AddNode(report.MakeNode("bar")) report3 := report.MakeReport() report3.Endpoint.AddNode(report.MakeNode("baz")) reports := []report.Report{ report1, report2, report3, } want := report.MakeReport() want.Endpoint. AddNode(report.MakeNode("foo")). AddNode(report.MakeNode("bar")). AddNode(report.MakeNode("baz")) for _, merger := range []app.Merger{app.MakeDumbMerger(), app.NewSmartMerger()} { // Test the empty list case if have := merger.Merge([]report.Report{}); !reflect.DeepEqual(have, report.MakeReport()) { t.Errorf("Bad merge: %s", test.Diff(have, want)) } if have := merger.Merge(reports); !reflect.DeepEqual(have, want) { t.Errorf("Bad merge: %s", test.Diff(have, want)) } // Repeat the above test to ensure caching works if have := merger.Merge(reports); !reflect.DeepEqual(have, want) { t.Errorf("Bad merge: %s", test.Diff(have, want)) } } }
func TestSmartMerger(t *testing.T) { // Use 3 reports _WITH SAME ID_ report1 := report.MakeReport() report1.Endpoint.AddNode(report.MakeNode("foo")) report1.ID = "foo" report2 := report.MakeReport() report2.Endpoint.AddNode(report.MakeNode("bar")) report2.ID = "foo" report3 := report.MakeReport() report3.Endpoint.AddNode(report.MakeNode("baz")) report3.ID = "foo" reports := []report.Report{ report1, report2, report3, } want := report.MakeReport() want.Endpoint.AddNode(report.MakeNode("foo")) merger := app.NewSmartMerger() if have := merger.Merge(reports); !reflect.DeepEqual(have, want) { t.Errorf("Bad merge: %s", test.Diff(have, want)) } }
func TestFilterUnconnectedSelf(t *testing.T) { // Test nodes that are only connected to themselves are filtered. { nodes := report.Nodes{ "foo": report.MakeNode("foo").WithAdjacent("foo"), } renderer := mockRenderer{Nodes: nodes} have := renderer.Render(report.MakeReport(), render.FilterUnconnected) if len(have) > 0 { t.Error("expected node only connected to self to be removed") } } }
func TestNode(t *testing.T) { { node := report.MakeNodeWith("foo", map[string]string{ "foo": "bar", }) if v, _ := node.Latest.Lookup("foo"); v != "bar" { t.Errorf("want foo, have %s", v) } } { node := report.MakeNode("foo").WithCounters( map[string]int{"foo": 1}, ) if value, _ := node.Counters.Lookup("foo"); value != 1 { t.Errorf("want foo, have %d", value) } } { node := report.MakeNode("foo").WithAdjacent("foo") if node.Adjacency[0] != "foo" { t.Errorf("want foo, have %v", node.Adjacency) } } { node := report.MakeNode("foo").WithEdge("foo", report.EdgeMetadata{ EgressPacketCount: newu64(13), }) if node.Adjacency[0] != "foo" { t.Errorf("want foo, have %v", node.Adjacency) } if v, ok := node.Edges.Lookup("foo"); ok && *v.EgressPacketCount != 13 { t.Errorf("want 13, have %v", node.Edges) } } }
func TestMapRender1(t *testing.T) { // 1. Check when we return false, the node gets filtered out mapper := render.Map{ MapFunc: func(nodes report.Node, _ report.Networks) report.Nodes { return report.Nodes{} }, Renderer: mockRenderer{Nodes: report.Nodes{ "foo": report.MakeNode("foo"), }}, } want := report.Nodes{} have := mapper.Render(report.MakeReport(), render.FilterNoop) if !reflect.DeepEqual(want, have) { t.Errorf("want %+v, have %+v", want, have) } }
func TestTables(t *testing.T) { want := map[string]string{ "foo1": "bar1", "foo2": "bar2", } nmd := report.MakeNode("foo1") nmd = nmd.AddTable("foo_", want) have, truncationCount := nmd.ExtractTable("foo_") if truncationCount != 0 { t.Error("Table shouldn't had been truncated") } if !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } }
func TestMemoise(t *testing.T) { calls := 0 r := renderFunc(func(rpt report.Report) report.Nodes { calls++ return report.Nodes{rpt.ID: report.MakeNode(rpt.ID)} }) m := render.Memoise(r) rpt1 := report.MakeReport() result1 := m.Render(rpt1, nil) // it should have rendered it. if _, ok := result1[rpt1.ID]; !ok { t.Errorf("Expected rendered report to contain a node, but got: %v", result1) } if calls != 1 { t.Errorf("Expected renderer to have been called the first time") } result2 := m.Render(rpt1, nil) if !reflect.DeepEqual(result1, result2) { t.Errorf("Expected memoised result to be returned: %s", test.Diff(result1, result2)) } if calls != 1 { t.Errorf("Expected renderer to not have been called the second time") } rpt2 := report.MakeReport() result3 := m.Render(rpt2, nil) if reflect.DeepEqual(result1, result3) { t.Errorf("Expected different result for different report, but were the same") } if calls != 2 { t.Errorf("Expected renderer to have been called again for a different report") } render.ResetCache() result4 := m.Render(rpt1, nil) if !reflect.DeepEqual(result1, result4) { t.Errorf("Expected original result to be returned: %s", test.Diff(result1, result4)) } if calls != 3 { t.Errorf("Expected renderer to have been called again after cache reset") } }
func (r *Reporter) processTopology() (report.Topology, error) { t := report.MakeTopology(). WithMetadataTemplates(MetadataTemplates). WithMetricTemplates(MetricTemplates) now := mtime.Now() deltaTotal, maxCPU, err := r.jiffies() if err != nil { return t, err } err = r.walker.Walk(func(p, prev Process) { pidstr := strconv.Itoa(p.PID) nodeID := report.MakeProcessNodeID(r.scope, pidstr) node := report.MakeNode(nodeID) for _, tuple := range []struct{ key, value string }{ {PID, pidstr}, {Name, p.Name}, {Cmdline, p.Cmdline}, {Threads, strconv.Itoa(p.Threads)}, } { if tuple.value != "" { node = node.WithLatests(map[string]string{tuple.key: tuple.value}) } } if p.PPID > 0 { node = node.WithLatests(map[string]string{PPID: strconv.Itoa(p.PPID)}) } if deltaTotal > 0 { cpuUsage := float64(p.Jiffies-prev.Jiffies) / float64(deltaTotal) * 100. node = node.WithMetric(CPUUsage, report.MakeMetric().Add(now, cpuUsage).WithMax(maxCPU)) } node = node.WithMetric(MemoryUsage, report.MakeMetric().Add(now, float64(p.RSSBytes)).WithMax(float64(p.RSSBytesLimit))) node = node.WithMetric(OpenFilesCount, report.MakeMetric().Add(now, float64(p.OpenFilesCount)).WithMax(float64(p.OpenFilesLimit))) t.AddNode(node) }) return t, err }
func TestInterpolateCounts(t *testing.T) { var ( hostID = "macbook-air" srcNodeID = report.MakeEndpointNodeID(hostID, "1.2.3.4", "5678") dstNodeID = report.MakeEndpointNodeID(hostID, "5.6.7.8", "9012") samplingCount = uint64(200) samplingTotal = uint64(2345) packetCount = uint64(123) byteCount = uint64(4096) ) r := report.MakeReport() r.Sampling.Count = samplingCount r.Sampling.Total = samplingTotal r.Endpoint.AddNode(report.MakeNode(srcNodeID).WithEdge(dstNodeID, report.EdgeMetadata{ EgressPacketCount: newu64(packetCount), IngressPacketCount: newu64(packetCount), EgressByteCount: newu64(byteCount), IngressByteCount: newu64(byteCount), })) interpolateCounts(r) var ( rate = float64(samplingCount) / float64(samplingTotal) factor = 1.0 / rate apply = func(v uint64) uint64 { return uint64(factor * float64(v)) } emd = r.Endpoint.Nodes[srcNodeID].Edges[dstNodeID] ) if want, have := apply(packetCount), (*emd.EgressPacketCount); want != have { t.Errorf("want %d packets, have %d", want, have) } if want, have := apply(packetCount), (*emd.IngressPacketCount); want != have { t.Errorf("want %d packets, have %d", want, have) } if want, have := apply(byteCount), (*emd.EgressByteCount); want != have { t.Errorf("want %d bytes, have %d", want, have) } if want, have := apply(byteCount), (*emd.IngressByteCount); want != have { t.Errorf("want %d bytes, have %d", want, have) } }
func TestNodeOrdering(t *testing.T) { ids := [][2]string{{}, {"a", "0"}, {"a", "1"}, {"b", "0"}, {"b", "1"}, {"c", "3"}} nodes := []report.Node{} for _, id := range ids { nodes = append(nodes, report.MakeNode(id[1]).WithTopology(id[0])) } for i, node := range nodes { if !node.Equal(node) { t.Errorf("Expected %q %q == %q %q, but was not", node.Topology, node.ID, node.Topology, node.ID) } if i > 0 { if !node.After(nodes[i-1]) { t.Errorf("Expected %q %q > %q %q, but was not", node.Topology, node.ID, nodes[i-1].Topology, nodes[i-1].ID) } if !nodes[i-1].Before(node) { t.Errorf("Expected %q %q < %q %q, but was not", nodes[i-1].Topology, nodes[i-1].ID, node.Topology, node.ID) } } } }
func TestCollectorExpire(t *testing.T) { now := time.Now() mtime.NowForce(now) defer mtime.NowReset() ctx := context.Background() window := 10 * time.Second c := app.NewCollector(window) // 1st check the collector is empty have, err := c.Report(ctx) if err != nil { t.Error(err) } if want := report.MakeReport(); !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } // Now check an added report is returned r1 := report.MakeReport() r1.Endpoint.AddNode(report.MakeNode("foo")) c.Add(ctx, r1) have, err = c.Report(ctx) if err != nil { t.Error(err) } if want := r1; !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } // Finally move time forward to expire the report mtime.NowForce(now.Add(window)) have, err = c.Report(ctx) if err != nil { t.Error(err) } if want := report.MakeReport(); !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } }
func TestTruncation(t *testing.T) { wantTruncationCount := 1 want := map[string]string{} for i := 0; i < report.MaxTableRows+wantTruncationCount; i++ { key := fmt.Sprintf("key%d", i) value := fmt.Sprintf("value%d", i) want[key] = value } nmd := report.MakeNode("foo1") nmd = nmd.AddTable("foo_", want) _, truncationCount := nmd.ExtractTable("foo_") if truncationCount != wantTruncationCount { t.Error( "Table should had been truncated by", wantTruncationCount, "and not", truncationCount, ) } }
func TestFilterUnconnectedPseudoNodes(t *testing.T) { // Test pseudo nodes that are made unconnected by filtering // are also removed. { nodes := report.Nodes{ "foo": report.MakeNode("foo").WithAdjacent("bar"), "bar": report.MakeNode("bar").WithAdjacent("baz"), "baz": report.MakeNode("baz").WithTopology(render.Pseudo), } renderer := mockRenderer{Nodes: nodes} filter := func(renderer render.Renderer) render.Renderer { return &render.Filter{ FilterFunc: func(node report.Node) bool { return true }, Renderer: renderer, } } want := nodes have := renderer.Render(report.MakeReport(), filter) if !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } } { filter := func(renderer render.Renderer) render.Renderer { return &render.Filter{ FilterFunc: func(node report.Node) bool { return node.ID != "bar" }, Renderer: renderer, } } renderer := mockRenderer{Nodes: report.Nodes{ "foo": report.MakeNode("foo").WithAdjacent("bar"), "bar": report.MakeNode("bar").WithAdjacent("baz"), "baz": report.MakeNode("baz").WithTopology(render.Pseudo), }} have := renderer.Render(report.MakeReport(), filter) if _, ok := have["baz"]; ok { t.Error("expected the unconnected pseudonode baz to have been removed") } } { filter := func(renderer render.Renderer) render.Renderer { return &render.Filter{ FilterFunc: func(node report.Node) bool { return node.ID != "bar" }, Renderer: renderer, } } renderer := mockRenderer{Nodes: report.Nodes{ "foo": report.MakeNode("foo"), "bar": report.MakeNode("bar").WithAdjacent("foo"), "baz": report.MakeNode("baz").WithTopology(render.Pseudo).WithAdjacent("bar"), }} have := renderer.Render(report.MakeReport(), filter) if _, ok := have["baz"]; ok { t.Error("expected the unconnected pseudonode baz to have been removed") } } }
func TestNodeMetrics(t *testing.T) { inputs := []struct { name string node report.Node want []report.MetricRow }{ { name: "process", node: fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID], want: []report.MetricRow{ { ID: process.CPUUsage, Label: "CPU", Format: "percent", Group: "", Value: 0.01, Priority: 1, Metric: &fixture.ClientProcess1CPUMetric, }, { ID: process.MemoryUsage, Label: "Memory", Format: "filesize", Group: "", Value: 0.02, Priority: 2, Metric: &fixture.ClientProcess1MemoryMetric, }, }, }, { name: "container", node: fixture.Report.Container.Nodes[fixture.ClientContainerNodeID], want: []report.MetricRow{ { ID: docker.CPUTotalUsage, Label: "CPU", Format: "percent", Group: "", Value: 0.03, Priority: 1, Metric: &fixture.ClientContainerCPUMetric, }, { ID: docker.MemoryUsage, Label: "Memory", Format: "filesize", Group: "", Value: 0.04, Priority: 2, Metric: &fixture.ClientContainerMemoryMetric, }, }, }, { name: "host", node: fixture.Report.Host.Nodes[fixture.ClientHostNodeID], want: []report.MetricRow{ { ID: host.CPUUsage, Label: "CPU", Format: "percent", Group: "", Value: 0.07, Priority: 1, Metric: &fixture.ClientHostCPUMetric, }, { ID: host.MemoryUsage, Label: "Memory", Format: "filesize", Group: "", Value: 0.08, Priority: 2, Metric: &fixture.ClientHostMemoryMetric, }, { ID: host.Load1, Label: "Load (1m)", Group: "load", Value: 0.09, Priority: 11, Metric: &fixture.ClientHostLoad1Metric, }, }, }, { name: "unknown topology", node: report.MakeNode(fixture.ClientContainerNodeID).WithTopology("foobar"), want: nil, }, } for _, input := range inputs { have := detailed.NodeMetrics(fixture.Report, input.node) if !reflect.DeepEqual(input.want, have) { t.Errorf("%s: %s", input.name, test.Diff(input.want, have)) } } }