func renderTo(rpt report.Report, topology string) (detailed.NodeSummaries, error) { renderer, ok := map[string]render.Renderer{ "processes": render.FilterUnconnected(render.ProcessWithContainerNameRenderer), "processes-by-name": render.FilterUnconnected(render.ProcessNameRenderer), "containers": render.ContainerWithImageNameRenderer, "containers-by-image": render.ContainerImageRenderer, "hosts": render.HostRenderer, }[topology] if !ok { return detailed.NodeSummaries{}, fmt.Errorf("unknown topology %v", topology) } return detailed.Summaries(rpt, renderer.Render(rpt, render.FilterNoop)), nil }
// Websocket for the full topology. func handleWebsocket( ctx context.Context, rep Reporter, w http.ResponseWriter, r *http.Request, ) { if err := r.ParseForm(); err != nil { respondWith(w, http.StatusInternalServerError, err.Error()) return } loop := websocketLoop if t := r.Form.Get("t"); t != "" { var err error if loop, err = time.ParseDuration(t); err != nil { respondWith(w, http.StatusBadRequest, t) return } } conn, err := xfer.Upgrade(w, r, nil) if err != nil { // log.Info("Upgrade:", err) return } defer conn.Close() quit := make(chan struct{}) go func(c xfer.Websocket) { for { // just discard everything the browser sends if _, _, err := c.ReadMessage(); err != nil { if !xfer.IsExpectedWSCloseError(err) { log.Println("err:", err) } close(quit) break } } }(conn) var ( previousTopo detailed.NodeSummaries tick = time.Tick(loop) wait = make(chan struct{}, 1) topologyID = mux.Vars(r)["topology"] ) rep.WaitOn(ctx, wait) defer rep.UnWait(ctx, wait) for { report, err := rep.Report(ctx) if err != nil { log.Errorf("Error generating report: %v", err) return } renderer, decorator, err := topologyRegistry.rendererForTopology(topologyID, r.Form, report) if err != nil { log.Errorf("Error generating report: %v", err) return } newTopo := detailed.Summaries(report, renderer.Render(report, decorator)) diff := detailed.TopoDiff(previousTopo, newTopo) previousTopo = newTopo if err := conn.WriteJSON(diff); err != nil { if !xfer.IsExpectedWSCloseError(err) { log.Errorf("cannot serialize topology diff: %s", err) } return } select { case <-wait: case <-tick: case <-quit: return } } }
// Full topology. func handleTopology(ctx context.Context, renderer render.Renderer, decorator render.Decorator, report report.Report, w http.ResponseWriter, r *http.Request) { respondWith(w, http.StatusOK, APITopology{ Nodes: detailed.Summaries(report, renderer.Render(report, decorator)), }) }
func TestSummaries(t *testing.T) { { // Just a convenient source of some rendered nodes have := detailed.Summaries(fixture.Report, render.ProcessRenderer.Render(fixture.Report, render.FilterNoop)) // The ids of the processes rendered above expectedIDs := []string{ fixture.ClientProcess1NodeID, fixture.ClientProcess2NodeID, fixture.ServerProcessNodeID, fixture.NonContainerProcessNodeID, render.IncomingInternetID, render.OutgoingInternetID, } sort.Strings(expectedIDs) // It should summarize each node ids := []string{} for id := range have { ids = append(ids, id) } sort.Strings(ids) if !reflect.DeepEqual(expectedIDs, ids) { t.Fatalf("Expected Summaries to have summarized every node in the process renderer: %v, but got %v", expectedIDs, ids) } } // It should summarize nodes' metrics { t1, t2 := mtime.Now().Add(-1*time.Minute), mtime.Now() metric := report.MakeMetric().Add(t1, 1).Add(t2, 2) input := fixture.Report.Copy() input.Process.Nodes[fixture.ClientProcess1NodeID] = input.Process.Nodes[fixture.ClientProcess1NodeID].WithMetrics(report.Metrics{process.CPUUsage: metric}) have := detailed.Summaries(input, render.ProcessRenderer.Render(input, render.FilterNoop)) node, ok := have[fixture.ClientProcess1NodeID] if !ok { t.Fatalf("Expected output to have the node we added the metric to") } var row report.MetricRow ok = false for _, metric := range node.Metrics { if metric.ID == process.CPUUsage { row = metric ok = true break } } if !ok { t.Fatalf("Expected node to have the metric we added") } // Our summarized MetricRow want := report.MetricRow{ ID: process.CPUUsage, Label: "CPU", Format: "percent", Value: 2, Priority: 1, Metric: &report.Metric{ Samples: nil, Min: metric.Min, Max: metric.Max, First: metric.First, Last: metric.Last, }, } if !reflect.DeepEqual(want, row) { t.Fatalf("Expected to have summarized the node's metrics: %s", test.Diff(want, row)) } } }