mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 02:00:43 +00:00
228 lines
5.9 KiB
Go
228 lines
5.9 KiB
Go
package render
|
|
|
|
import (
|
|
"github.com/weaveworks/scope/report"
|
|
)
|
|
|
|
// MapFunc is anything which can take an arbitrary Node and
|
|
// return a set of other Nodes.
|
|
//
|
|
// If the output is empty, the node shall be omitted from the rendered topology.
|
|
type MapFunc func(report.Node, report.Networks) report.Nodes
|
|
|
|
// Renderer is something that can render a report to a set of Nodes.
|
|
type Renderer interface {
|
|
Render(report.Report) Nodes
|
|
}
|
|
|
|
// Nodes is the result of Rendering
|
|
type Nodes struct {
|
|
report.Nodes
|
|
Filtered int
|
|
}
|
|
|
|
// Merge merges the results of Rendering
|
|
func (r Nodes) Merge(o Nodes) Nodes {
|
|
return Nodes{
|
|
Nodes: r.Nodes.Merge(o.Nodes),
|
|
Filtered: r.Filtered + o.Filtered,
|
|
}
|
|
}
|
|
|
|
// Decorate renders the report with a decorated renderer
|
|
func Decorate(rpt report.Report, renderer Renderer, dct Decorator) Nodes {
|
|
if dct != nil {
|
|
renderer = dct(renderer)
|
|
}
|
|
return renderer.Render(rpt)
|
|
}
|
|
|
|
// Reduce renderer is a Renderer which merges together the output of several
|
|
// other renderers.
|
|
type Reduce []Renderer
|
|
|
|
// MakeReduce is the only sane way to produce a Reduce Renderer.
|
|
func MakeReduce(renderers ...Renderer) Renderer {
|
|
return Reduce(renderers)
|
|
}
|
|
|
|
// Render produces a set of Nodes given a Report.
|
|
func (r Reduce) Render(rpt report.Report) Nodes {
|
|
l := len(r)
|
|
switch l {
|
|
case 0:
|
|
return Nodes{}
|
|
}
|
|
c := make(chan Nodes, l)
|
|
for _, renderer := range r {
|
|
renderer := renderer // Pike!!
|
|
go func() {
|
|
c <- renderer.Render(rpt)
|
|
}()
|
|
}
|
|
for ; l > 1; l-- {
|
|
left, right := <-c, <-c
|
|
go func() {
|
|
c <- left.Merge(right)
|
|
}()
|
|
}
|
|
return <-c
|
|
}
|
|
|
|
// Map is a Renderer which produces a set of Nodes from the set of
|
|
// Nodes produced by another Renderer.
|
|
type Map struct {
|
|
MapFunc
|
|
Renderer
|
|
}
|
|
|
|
// MakeMap makes a new Map
|
|
func MakeMap(f MapFunc, r Renderer) Renderer {
|
|
return Map{f, r}
|
|
}
|
|
|
|
// Render transforms a set of Nodes produces by another Renderer.
|
|
// using a map function
|
|
func (m Map) Render(rpt report.Report) Nodes {
|
|
var (
|
|
input = m.Renderer.Render(rpt)
|
|
output = report.Nodes{}
|
|
mapped = map[string]report.IDList{} // input node ID -> output node IDs
|
|
adjacencies = map[string]report.IDList{} // output node ID -> input node Adjacencies
|
|
localNetworks = LocalNetworks(rpt)
|
|
)
|
|
|
|
// Rewrite all the nodes according to the map function
|
|
for _, inRenderable := range input.Nodes {
|
|
for _, outRenderable := range m.MapFunc(inRenderable, localNetworks) {
|
|
if existing, ok := output[outRenderable.ID]; ok {
|
|
outRenderable = outRenderable.Merge(existing)
|
|
}
|
|
|
|
output[outRenderable.ID] = outRenderable
|
|
mapped[inRenderable.ID] = mapped[inRenderable.ID].Add(outRenderable.ID)
|
|
adjacencies[outRenderable.ID] = adjacencies[outRenderable.ID].Merge(inRenderable.Adjacency)
|
|
}
|
|
}
|
|
|
|
// Rewrite Adjacency for new node IDs.
|
|
for outNodeID, inAdjacency := range adjacencies {
|
|
outAdjacency := report.MakeIDList()
|
|
for _, inAdjacent := range inAdjacency {
|
|
outAdjacency = outAdjacency.Merge(mapped[inAdjacent])
|
|
}
|
|
outNode := output[outNodeID]
|
|
outNode.Adjacency = outAdjacency
|
|
output[outNodeID] = outNode
|
|
}
|
|
|
|
return Nodes{Nodes: output}
|
|
}
|
|
|
|
// Decorator transforms one renderer to another. e.g. Filters.
|
|
type Decorator func(Renderer) Renderer
|
|
|
|
// ComposeDecorators composes decorators into one.
|
|
func ComposeDecorators(decorators ...Decorator) Decorator {
|
|
return func(r Renderer) Renderer {
|
|
for _, decorator := range decorators {
|
|
r = decorator(r)
|
|
}
|
|
return r
|
|
}
|
|
}
|
|
|
|
func propagateLatest(key string, from, to report.Node) report.Node {
|
|
if value, timestamp, ok := from.Latest.LookupEntry(key); ok {
|
|
to.Latest = to.Latest.Set(key, timestamp, value)
|
|
}
|
|
return to
|
|
}
|
|
|
|
// Condition is a predecate over the entire report that can evaluate to true or false.
|
|
type Condition func(report.Report) bool
|
|
|
|
type conditionalRenderer struct {
|
|
Condition
|
|
Renderer
|
|
}
|
|
|
|
// ConditionalRenderer renders nothing if the condition is false, otherwise it defers
|
|
// to the wrapped Renderer.
|
|
func ConditionalRenderer(c Condition, r Renderer) Renderer {
|
|
return conditionalRenderer{c, r}
|
|
}
|
|
|
|
func (cr conditionalRenderer) Render(rpt report.Report) Nodes {
|
|
if cr.Condition(rpt) {
|
|
return cr.Renderer.Render(rpt)
|
|
}
|
|
return Nodes{}
|
|
}
|
|
|
|
// ConstantRenderer renders a fixed set of nodes
|
|
type ConstantRenderer struct {
|
|
Nodes
|
|
}
|
|
|
|
// Render implements Renderer
|
|
func (c ConstantRenderer) Render(_ report.Report) Nodes {
|
|
return c.Nodes
|
|
}
|
|
|
|
// joinResults is used by Renderers that join sets of nodes
|
|
type joinResults struct {
|
|
nodes report.Nodes
|
|
mapped map[string]string // input node ID -> output node ID
|
|
}
|
|
|
|
func newJoinResults() joinResults {
|
|
return joinResults{nodes: make(report.Nodes), mapped: map[string]string{}}
|
|
}
|
|
|
|
// Add Node M under id, creating a new result node if not already there
|
|
// and updating the mapping from old ID to new ID
|
|
// Note we do not update any counters for child topologies here, because addToResults
|
|
// is only ever called when m is an endpoint and we never look at endpoint counts
|
|
func (ret *joinResults) addToResults(m report.Node, id string, create func(string) report.Node) {
|
|
result, exists := ret.nodes[id]
|
|
if !exists {
|
|
result = create(id)
|
|
}
|
|
result.Children = result.Children.Add(m)
|
|
result.Children = result.Children.Merge(m.Children)
|
|
ret.nodes[id] = result
|
|
ret.mapped[m.ID] = id
|
|
}
|
|
|
|
// Rewrite Adjacency for new nodes in ret for original nodes in input
|
|
func (ret *joinResults) fixupAdjacencies(input Nodes) {
|
|
for _, n := range input.Nodes {
|
|
outID, ok := ret.mapped[n.ID]
|
|
if !ok {
|
|
continue
|
|
}
|
|
out := ret.nodes[outID]
|
|
// for each adjacency in the original node, find out what it maps to (if any),
|
|
// and add that to the new node
|
|
for _, a := range n.Adjacency {
|
|
if mappedDest, found := ret.mapped[a]; found {
|
|
out.Adjacency = out.Adjacency.Add(mappedDest)
|
|
}
|
|
}
|
|
ret.nodes[outID] = out
|
|
}
|
|
}
|
|
|
|
func (ret *joinResults) copyUnmatched(input Nodes) {
|
|
for _, n := range input.Nodes {
|
|
if _, found := ret.nodes[n.ID]; !found {
|
|
ret.nodes[n.ID] = n
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ret *joinResults) result() Nodes {
|
|
return Nodes{Nodes: ret.nodes}
|
|
}
|