mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 10:11:03 +00:00
158 lines
5.0 KiB
Go
158 lines
5.0 KiB
Go
package render
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/weaveworks/scope/report"
|
|
)
|
|
|
|
// Renderer is something that can render a report to a set of RenderableNodes
|
|
type Renderer interface {
|
|
Render(report.Report) report.RenderableNodes
|
|
AggregateMetadata(rpt report.Report, localID, remoteID string) report.AggregateMetadata
|
|
}
|
|
|
|
// Reduce renderer is a Renderer which merges together the output of several
|
|
// other renderers
|
|
type Reduce []Renderer
|
|
|
|
// Render produces a set of RenderableNodes given a Report
|
|
func (r Reduce) Render(rpt report.Report) report.RenderableNodes {
|
|
result := report.RenderableNodes{}
|
|
for _, renderer := range r {
|
|
result.Merge(renderer.Render(rpt))
|
|
}
|
|
return result
|
|
}
|
|
|
|
// AggregateMetadata produces an AggregateMetadata for a given edge
|
|
func (r Reduce) AggregateMetadata(rpt report.Report, localID, remoteID string) report.AggregateMetadata {
|
|
metadata := report.AggregateMetadata{}
|
|
for _, renderer := range r {
|
|
metadata.Merge(renderer.AggregateMetadata(rpt, localID, remoteID))
|
|
}
|
|
return metadata
|
|
}
|
|
|
|
// Map is a Renderer which produces a set of RendererNodes by using a
|
|
// Mapper functions and topology selector.
|
|
type Map struct {
|
|
Selector report.TopologySelector
|
|
Mapper MapFunc
|
|
Pseudo PseudoFunc
|
|
}
|
|
|
|
// Render produces a set of RenderableNodes given a Report
|
|
func (m Map) Render(rpt report.Report) report.RenderableNodes {
|
|
return renderTopology(m.Selector(rpt), m.Mapper, m.Pseudo)
|
|
}
|
|
|
|
// RenderBy transforms a given Topology into a set of RenderableNodes, which
|
|
// the UI will render collectively as a graph. Note that a RenderableNode will
|
|
// always be rendered with other nodes, and therefore contains limited detail.
|
|
//
|
|
// RenderBy takes a a MapFunc, which defines how to group and label nodes. Npdes
|
|
// with the same mapped IDs will be merged.
|
|
func renderTopology(t report.Topology, mapFunc MapFunc, pseudoFunc PseudoFunc) report.RenderableNodes {
|
|
nodes := report.RenderableNodes{}
|
|
|
|
// 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.
|
|
var (
|
|
source2mapped = map[string]string{} // source node ID -> mapped node ID
|
|
source2host = map[string]string{} // source node ID -> origin host ID
|
|
)
|
|
for nodeID, metadata := range t.NodeMetadatas {
|
|
mapped, ok := mapFunc(metadata)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// 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.
|
|
existing, ok := nodes[mapped.ID]
|
|
if ok {
|
|
mapped.Merge(existing)
|
|
}
|
|
|
|
mapped.Origins = mapped.Origins.Add(nodeID)
|
|
nodes[mapped.ID] = mapped
|
|
source2mapped[nodeID] = mapped.ID
|
|
source2host[nodeID] = metadata[report.HostNodeID]
|
|
}
|
|
|
|
// Walk the graph and make connections.
|
|
for src, dsts := range t.Adjacency {
|
|
var (
|
|
srcNodeID, ok = report.ParseAdjacencyID(src)
|
|
//srcOriginHostID, _, ok2 = ParseNodeID(srcNodeID)
|
|
srcHostNodeID = source2host[srcNodeID]
|
|
srcRenderableID = source2mapped[srcNodeID] // must exist
|
|
srcRenderableNode = nodes[srcRenderableID] // must exist
|
|
)
|
|
if !ok {
|
|
log.Printf("bad adjacency ID %q", src)
|
|
continue
|
|
}
|
|
|
|
for _, dstNodeID := range dsts {
|
|
dstRenderableID, ok := source2mapped[dstNodeID]
|
|
if !ok {
|
|
pseudoNode, ok := pseudoFunc(srcNodeID, srcRenderableNode, dstNodeID)
|
|
if !ok {
|
|
continue
|
|
}
|
|
dstRenderableID = pseudoNode.ID
|
|
nodes[dstRenderableID] = pseudoNode
|
|
source2mapped[dstNodeID] = dstRenderableID
|
|
}
|
|
|
|
srcRenderableNode.Adjacency = srcRenderableNode.Adjacency.Add(dstRenderableID)
|
|
srcRenderableNode.Origins = srcRenderableNode.Origins.Add(srcHostNodeID)
|
|
srcRenderableNode.Origins = srcRenderableNode.Origins.Add(srcNodeID)
|
|
edgeID := report.MakeEdgeID(srcNodeID, dstNodeID)
|
|
if md, ok := t.EdgeMetadatas[edgeID]; ok {
|
|
srcRenderableNode.Metadata.Merge(md.Transform())
|
|
}
|
|
}
|
|
|
|
nodes[srcRenderableID] = srcRenderableNode
|
|
}
|
|
|
|
return nodes
|
|
}
|
|
|
|
// AggregateMetadata produces an AggregateMetadata for a given edge
|
|
func (m Map) AggregateMetadata(rpt report.Report, localID, remoteID string) report.AggregateMetadata {
|
|
return edgeMetadata(m.Selector(rpt), m.Mapper, localID, remoteID).Transform()
|
|
}
|
|
|
|
// EdgeMetadata gives the metadata of an edge from the perspective of the
|
|
// srcRenderableID. Since an edgeID can have multiple edges on the address
|
|
// level, it uses the supplied mapping function to translate address IDs to
|
|
// renderable node (mapped) IDs.
|
|
func edgeMetadata(t report.Topology, mapFunc MapFunc, srcRenderableID, dstRenderableID string) report.EdgeMetadata {
|
|
metadata := report.EdgeMetadata{}
|
|
for edgeID, edgeMeta := range t.EdgeMetadatas {
|
|
src, dst, ok := report.ParseEdgeID(edgeID)
|
|
if !ok {
|
|
log.Printf("bad edge ID %q", edgeID)
|
|
continue
|
|
}
|
|
if src != report.TheInternet {
|
|
mapped, _ := mapFunc(t.NodeMetadatas[src])
|
|
src = mapped.ID
|
|
}
|
|
if dst != report.TheInternet {
|
|
mapped, _ := mapFunc(t.NodeMetadatas[dst])
|
|
dst = mapped.ID
|
|
}
|
|
if src == srcRenderableID && dst == dstRenderableID {
|
|
metadata.Flatten(edgeMeta)
|
|
}
|
|
}
|
|
return metadata
|
|
}
|